@emberai-engg/task-board 0.3.6 → 0.4.1
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 +132 -71
- package/dist/index.d.mts +561 -2
- package/dist/index.d.ts +561 -2
- package/dist/index.js +3458 -36
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3400 -35
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +2450 -25
- package/package.json +6 -4
package/dist/index.d.ts
CHANGED
|
@@ -6,12 +6,29 @@ interface Project {
|
|
|
6
6
|
slug: string;
|
|
7
7
|
name: string;
|
|
8
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* The four description sections that support inline editing, Draft/Approved
|
|
11
|
+
* status, and highlight-to-comment anchors.
|
|
12
|
+
*
|
|
13
|
+
* `open_questions` is kept on `StructuredDescription` for backwards
|
|
14
|
+
* compatibility but is no longer surfaced in the description UI — the
|
|
15
|
+
* Outstanding Questions section uses a structured collection instead.
|
|
16
|
+
*/
|
|
17
|
+
type SectionKey = "problem" | "user_story" | "proposed_behavior" | "acceptance_criteria";
|
|
18
|
+
type SectionStatus = "draft" | "approved";
|
|
9
19
|
interface StructuredDescription {
|
|
10
20
|
problem: string;
|
|
11
21
|
user_story: string;
|
|
12
22
|
proposed_behavior: string;
|
|
13
23
|
acceptance_criteria: string;
|
|
24
|
+
/**
|
|
25
|
+
* Legacy free-text questions field. Kept for backwards compatibility with
|
|
26
|
+
* tasks created before the structured Outstanding Questions feature.
|
|
27
|
+
* Not rendered in the current detail UI.
|
|
28
|
+
*/
|
|
14
29
|
open_questions: string;
|
|
30
|
+
/** Per-section review status, e.g. `{ problem: "approved" }`. Defaults to draft when absent. */
|
|
31
|
+
section_status?: Record<string, SectionStatus>;
|
|
15
32
|
}
|
|
16
33
|
interface Task {
|
|
17
34
|
id: string;
|
|
@@ -38,6 +55,11 @@ interface ActivityEntry {
|
|
|
38
55
|
user_name: string;
|
|
39
56
|
created_at: string;
|
|
40
57
|
}
|
|
58
|
+
type ThreadStatus = "active" | "complete";
|
|
59
|
+
interface ThreadAnchor {
|
|
60
|
+
section: SectionKey;
|
|
61
|
+
snippet: string;
|
|
62
|
+
}
|
|
41
63
|
interface Comment {
|
|
42
64
|
id: string;
|
|
43
65
|
task_id: string;
|
|
@@ -48,6 +70,50 @@ interface Comment {
|
|
|
48
70
|
edited?: boolean;
|
|
49
71
|
edited_at?: string;
|
|
50
72
|
created_at: string;
|
|
73
|
+
/** Set on replies; null/undefined for top-level (thread root) comments. */
|
|
74
|
+
parent_id?: string | null;
|
|
75
|
+
/** Top-level (thread) metadata. Ignored on replies. */
|
|
76
|
+
title?: string | null;
|
|
77
|
+
thread_status?: ThreadStatus;
|
|
78
|
+
anchor?: ThreadAnchor | null;
|
|
79
|
+
attachment_ids?: string[];
|
|
80
|
+
}
|
|
81
|
+
type QuestionStatus = "awaiting" | "answered";
|
|
82
|
+
interface QuestionReply {
|
|
83
|
+
id: string;
|
|
84
|
+
question_id: string;
|
|
85
|
+
task_id: string;
|
|
86
|
+
content: string;
|
|
87
|
+
author_id: string;
|
|
88
|
+
author_name: string;
|
|
89
|
+
created_at: string;
|
|
90
|
+
}
|
|
91
|
+
interface Question {
|
|
92
|
+
id: string;
|
|
93
|
+
task_id: string;
|
|
94
|
+
text: string;
|
|
95
|
+
status: QuestionStatus;
|
|
96
|
+
asked_by: string;
|
|
97
|
+
asked_by_name: string;
|
|
98
|
+
answered_by_reply_id?: string | null;
|
|
99
|
+
replies: QuestionReply[];
|
|
100
|
+
created_at: string;
|
|
101
|
+
updated_at: string;
|
|
102
|
+
}
|
|
103
|
+
type AttachmentKind = "image" | "file" | "link";
|
|
104
|
+
interface Attachment {
|
|
105
|
+
id: string;
|
|
106
|
+
task_id: string;
|
|
107
|
+
kind: AttachmentKind;
|
|
108
|
+
name: string;
|
|
109
|
+
size?: number | null;
|
|
110
|
+
content_type?: string | null;
|
|
111
|
+
/** Read URL — signed GCS URL for image/file uploads, raw URL for links. */
|
|
112
|
+
url: string;
|
|
113
|
+
storage_path?: string | null;
|
|
114
|
+
uploaded_by: string;
|
|
115
|
+
uploaded_by_name: string;
|
|
116
|
+
created_at: string;
|
|
51
117
|
}
|
|
52
118
|
interface Notification {
|
|
53
119
|
id: string;
|
|
@@ -73,13 +139,19 @@ type ColumnUnreads = Record<string, number>;
|
|
|
73
139
|
interface ColumnConfig {
|
|
74
140
|
key: string;
|
|
75
141
|
label: string;
|
|
142
|
+
/** Tailwind class for the round status dot, e.g. `bg-amber-500`. */
|
|
76
143
|
color: string;
|
|
144
|
+
/** Optional Tailwind classes for the inline status pill (background + text). */
|
|
145
|
+
chip?: string;
|
|
77
146
|
description: string;
|
|
78
147
|
}
|
|
79
148
|
interface PriorityConfig {
|
|
80
149
|
value: string;
|
|
81
150
|
label: string;
|
|
151
|
+
/** Tailwind classes for the priority pill (bg + text + border). */
|
|
82
152
|
className: string;
|
|
153
|
+
/** Optional Tailwind class for a small priority dot, e.g. `bg-amber-500`. */
|
|
154
|
+
dot?: string;
|
|
83
155
|
}
|
|
84
156
|
interface TagConfig {
|
|
85
157
|
value: string;
|
|
@@ -87,8 +159,10 @@ interface TagConfig {
|
|
|
87
159
|
className: string;
|
|
88
160
|
}
|
|
89
161
|
interface DescriptionSectionConfig {
|
|
90
|
-
key:
|
|
162
|
+
key: SectionKey;
|
|
91
163
|
label: string;
|
|
164
|
+
/** Placeholder text shown in the editor when the section is empty. */
|
|
165
|
+
placeholder?: string;
|
|
92
166
|
}
|
|
93
167
|
interface TaskBoardUser {
|
|
94
168
|
username: string;
|
|
@@ -130,6 +204,10 @@ interface ColumnResponse {
|
|
|
130
204
|
interface TaskDetailResponse extends Task {
|
|
131
205
|
comments: Comment[];
|
|
132
206
|
activity: ActivityEntry[];
|
|
207
|
+
/** Embedded structured questions (with their replies). */
|
|
208
|
+
questions?: Question[];
|
|
209
|
+
/** Embedded attachments with read URLs already computed server-side. */
|
|
210
|
+
attachments?: Attachment[];
|
|
133
211
|
}
|
|
134
212
|
interface NotificationCountResponse {
|
|
135
213
|
count: number;
|
|
@@ -153,10 +231,36 @@ interface UpdateTaskPayload {
|
|
|
153
231
|
interface CreateCommentPayload {
|
|
154
232
|
content: string;
|
|
155
233
|
is_internal?: boolean;
|
|
234
|
+
/** Set on replies. Top-level (thread) comments leave this null/undefined. */
|
|
235
|
+
parent_id?: string | null;
|
|
236
|
+
/** Optional title for top-level threads. */
|
|
237
|
+
title?: string | null;
|
|
238
|
+
/** Anchor metadata for highlight-to-comment threads. */
|
|
239
|
+
anchor?: ThreadAnchor | null;
|
|
240
|
+
/** Attachment IDs referenced from this comment. */
|
|
241
|
+
attachment_ids?: string[];
|
|
156
242
|
}
|
|
157
243
|
interface EditCommentPayload {
|
|
158
244
|
content: string;
|
|
159
245
|
}
|
|
246
|
+
interface UpdateThreadPayload {
|
|
247
|
+
title?: string;
|
|
248
|
+
thread_status?: ThreadStatus;
|
|
249
|
+
}
|
|
250
|
+
interface CreateQuestionPayload {
|
|
251
|
+
text: string;
|
|
252
|
+
}
|
|
253
|
+
interface UpdateQuestionPayload {
|
|
254
|
+
text?: string;
|
|
255
|
+
status?: QuestionStatus;
|
|
256
|
+
}
|
|
257
|
+
interface CreateQuestionReplyPayload {
|
|
258
|
+
content: string;
|
|
259
|
+
}
|
|
260
|
+
interface CreateLinkAttachmentPayload {
|
|
261
|
+
url: string;
|
|
262
|
+
name?: string;
|
|
263
|
+
}
|
|
160
264
|
|
|
161
265
|
interface TaskBoardProps {
|
|
162
266
|
/** Optional class name for the outer container */
|
|
@@ -284,6 +388,355 @@ interface TaskDetailPanelProps {
|
|
|
284
388
|
}
|
|
285
389
|
declare function TaskDetailPanel({ task, projectSlug, onClose, onUpdate }: TaskDetailPanelProps): react_jsx_runtime.JSX.Element;
|
|
286
390
|
|
|
391
|
+
interface TaskDetailViewProps {
|
|
392
|
+
taskId: string;
|
|
393
|
+
/** href for the "Back to Task Board" link. Ignored if `onBack` is provided. */
|
|
394
|
+
backHref?: string;
|
|
395
|
+
/** Called when the back link is clicked. Use this for SPA routing. */
|
|
396
|
+
onBack?: () => void;
|
|
397
|
+
/** Optional slot above the main content (e.g. consumer's breadcrumb bar). */
|
|
398
|
+
breadcrumb?: React__default.ReactNode;
|
|
399
|
+
/** Called after a successful delete; consumer should navigate away. */
|
|
400
|
+
onDeleted?: (task: Task) => void;
|
|
401
|
+
/**
|
|
402
|
+
* Provide this to enable the prev/next buttons. The view fetches the
|
|
403
|
+
* project's task list and computes neighbors; on click, the consumer
|
|
404
|
+
* is responsible for navigating to /task-board/[id]?project=<slug>.
|
|
405
|
+
*/
|
|
406
|
+
onNavigateToTask?: (taskId: string, projectSlug: string) => void;
|
|
407
|
+
/** Build a share URL for the task. Defaults to `${origin}/task-board/${id}?project=${slug}`. */
|
|
408
|
+
buildShareUrl?: (task: Task) => string;
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Full-page task detail orchestrator. Renders the breadcrumb slot, back +
|
|
412
|
+
* prev/next chrome, the task ID/project header, inline-editable title,
|
|
413
|
+
* status/priority/menu action cluster, all description sections, outstanding
|
|
414
|
+
* questions, attachments, and the right-side threads panel with
|
|
415
|
+
* highlight-to-comment.
|
|
416
|
+
*
|
|
417
|
+
* Consumers are responsible for app chrome (sidebar, breadcrumb bar) — pass
|
|
418
|
+
* the breadcrumb via `breadcrumb` and place this inside your existing layout.
|
|
419
|
+
*/
|
|
420
|
+
declare function TaskDetailView({ taskId, backHref, onBack, breadcrumb, onDeleted, onNavigateToTask, buildShareUrl, }: TaskDetailViewProps): react_jsx_runtime.JSX.Element;
|
|
421
|
+
|
|
422
|
+
interface DescriptionSectionProps {
|
|
423
|
+
sectionKey: SectionKey;
|
|
424
|
+
label: string;
|
|
425
|
+
placeholder: string;
|
|
426
|
+
/** Markdown content. */
|
|
427
|
+
value: string;
|
|
428
|
+
/** Called with the new markdown when the user commits an edit. */
|
|
429
|
+
onChange: (next: string) => void;
|
|
430
|
+
/** Current Draft/Approved status; defaults to "draft" when omitted. */
|
|
431
|
+
status: SectionStatus;
|
|
432
|
+
/** Toggle Draft ↔ Approved. */
|
|
433
|
+
onStatusChange: (next: SectionStatus) => void;
|
|
434
|
+
/** Optional saving indicator passed through to the editor. */
|
|
435
|
+
saving?: boolean;
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* One free-text description section (Problem / User Story / etc.) with:
|
|
439
|
+
* - segmented Draft/Approved toggle and hover-card explanations
|
|
440
|
+
* - read-mode `MarkdownView`
|
|
441
|
+
* - click-to-focus WYSIWYG `MarkdownEditor`
|
|
442
|
+
* - `data-section={sectionKey}` so highlight-to-comment can anchor here
|
|
443
|
+
*/
|
|
444
|
+
declare function DescriptionSection({ sectionKey, label, placeholder, value, onChange, status, onStatusChange, saving, }: DescriptionSectionProps): react_jsx_runtime.JSX.Element;
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Lightweight read-mode markdown renderer for description sections, thread
|
|
448
|
+
* messages, and other static markdown content. Supports H1/H2/H3, bullet lists,
|
|
449
|
+
* numbered lists, blockquotes, task lists (`- [ ] foo`), inline bold (`**`),
|
|
450
|
+
* inline code, and `@[Name](username)` mentions rendered as styled pills.
|
|
451
|
+
*
|
|
452
|
+
* For editing, use `MarkdownEditor` (WYSIWYG, contenteditable).
|
|
453
|
+
*/
|
|
454
|
+
declare function MarkdownView({ content, className }: {
|
|
455
|
+
content: string;
|
|
456
|
+
className?: string;
|
|
457
|
+
}): react_jsx_runtime.JSX.Element | null;
|
|
458
|
+
|
|
459
|
+
interface MarkdownEditorProps {
|
|
460
|
+
/** Markdown content. Used once on mount; the contenteditable owns state from then on. */
|
|
461
|
+
value: string;
|
|
462
|
+
/** Called with the canonical markdown when the user blurs the editor. */
|
|
463
|
+
onCommit: (md: string) => void;
|
|
464
|
+
/** Called when the user presses Escape. */
|
|
465
|
+
onCancel: () => void;
|
|
466
|
+
/** Placeholder shown when the editor is empty. */
|
|
467
|
+
placeholder: string;
|
|
468
|
+
/** Optional saving indicator. */
|
|
469
|
+
saving?: boolean;
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* WYSIWYG markdown editor. Uses a contenteditable div with execCommand
|
|
473
|
+
* formatting so users never see raw `**` / `- ` / `# ` syntax. Storage
|
|
474
|
+
* format remains markdown via `mdToHtml`/`htmlToMd` round-trip helpers,
|
|
475
|
+
* so existing readers (`MarkdownView`, server-side previews) keep working.
|
|
476
|
+
*
|
|
477
|
+
* Pair with the CSS in `task-board.css` (`.eb-tb-markdown-editor`).
|
|
478
|
+
*/
|
|
479
|
+
declare function MarkdownEditor({ value, onCommit, onCancel, placeholder, saving }: MarkdownEditorProps): react_jsx_runtime.JSX.Element;
|
|
480
|
+
|
|
481
|
+
interface OutstandingQuestionsSectionProps {
|
|
482
|
+
/** All questions for the task. */
|
|
483
|
+
questions: Question[];
|
|
484
|
+
/** Username of the logged-in user, for permission checks (delete-own). */
|
|
485
|
+
currentUsername: string;
|
|
486
|
+
/** Create a new question. */
|
|
487
|
+
onCreate: (text: string) => Promise<void> | void;
|
|
488
|
+
/** Toggle question status (awaiting ↔ answered). */
|
|
489
|
+
onSetStatus: (questionId: string, status: 'awaiting' | 'answered') => Promise<void> | void;
|
|
490
|
+
/** Delete a question (and its replies). */
|
|
491
|
+
onDelete: (questionId: string) => Promise<void> | void;
|
|
492
|
+
/** Add a reply to a question. */
|
|
493
|
+
onAddReply: (questionId: string, content: string) => Promise<void> | void;
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Structured list of outstanding questions with awaiting/answered filter,
|
|
497
|
+
* inline composer, and per-question collapsible threads. Replaces the legacy
|
|
498
|
+
* free-text `open_questions` description field.
|
|
499
|
+
*/
|
|
500
|
+
declare function OutstandingQuestionsSection({ questions, currentUsername, onCreate, onSetStatus, onDelete, onAddReply, }: OutstandingQuestionsSectionProps): react_jsx_runtime.JSX.Element;
|
|
501
|
+
|
|
502
|
+
interface AttachmentsSectionProps {
|
|
503
|
+
attachments: Attachment[];
|
|
504
|
+
/** Upload a file (image or document). */
|
|
505
|
+
onUpload: (file: File) => Promise<unknown> | void;
|
|
506
|
+
/** Add an external link/recording. */
|
|
507
|
+
onAddLink: (url: string, name?: string) => Promise<unknown> | void;
|
|
508
|
+
/** Delete an attachment by id. */
|
|
509
|
+
onDelete: (attachmentId: string) => Promise<unknown> | void;
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Three-subgroup attachments display (Images / Files / Links & recordings)
|
|
513
|
+
* with an "Add attachment" popover and an "Add link or recording" modal.
|
|
514
|
+
*
|
|
515
|
+
* Bug #3 fix: Cancel/Save buttons in the link modal use `inline-flex
|
|
516
|
+
* items-center justify-center` (without these, the button text wasn't
|
|
517
|
+
* vertically centered) and `text-[13px]` (was `text-[12px]`).
|
|
518
|
+
*/
|
|
519
|
+
declare function AttachmentsSection({ attachments, onUpload, onAddLink, onDelete, }: AttachmentsSectionProps): react_jsx_runtime.JSX.Element;
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* UI shape derived from Comment + Attachment data. A `Thread` is a top-level
|
|
523
|
+
* comment (no `parent_id`) augmented with metadata, replies, and resolved
|
|
524
|
+
* attachment objects (looked up by id).
|
|
525
|
+
*/
|
|
526
|
+
interface Thread {
|
|
527
|
+
id: string;
|
|
528
|
+
title: string;
|
|
529
|
+
/** True when title was derived from content rather than persisted on the row. */
|
|
530
|
+
titleDerived: boolean;
|
|
531
|
+
preview: string;
|
|
532
|
+
author_id: string;
|
|
533
|
+
author_name: string;
|
|
534
|
+
created_at: string;
|
|
535
|
+
is_internal: boolean;
|
|
536
|
+
status: ThreadStatus;
|
|
537
|
+
/** Original mention-tagged text, used when rendering the message. */
|
|
538
|
+
rawContent: string;
|
|
539
|
+
anchor?: ThreadAnchor | null;
|
|
540
|
+
attachments: Attachment[];
|
|
541
|
+
replies: ThreadReply[];
|
|
542
|
+
}
|
|
543
|
+
interface ThreadReply {
|
|
544
|
+
id: string;
|
|
545
|
+
author_id: string;
|
|
546
|
+
author_name: string;
|
|
547
|
+
content: string;
|
|
548
|
+
created_at: string;
|
|
549
|
+
is_internal: boolean;
|
|
550
|
+
}
|
|
551
|
+
/** Group a flat comments list into threads (top-level + replies + resolved attachments). */
|
|
552
|
+
declare function deriveThreads(comments: Comment[], attachments: Attachment[]): Thread[];
|
|
553
|
+
/** Display label for a description section key, falling back to the key itself. */
|
|
554
|
+
declare function sectionLabel(key: SectionKey): string;
|
|
555
|
+
/** Compact relative time like "5m ago", "3h ago", "2d ago", or a short date. */
|
|
556
|
+
declare function timeAgo(dateStr: string): string;
|
|
557
|
+
|
|
558
|
+
interface ThreadsPanelProps {
|
|
559
|
+
threads: Thread[];
|
|
560
|
+
activity: ActivityEntry[];
|
|
561
|
+
attachments: Attachment[];
|
|
562
|
+
/** Open / close state. Lift this to the page so the bubble + main padding can coordinate. */
|
|
563
|
+
open: boolean;
|
|
564
|
+
onToggle: () => void;
|
|
565
|
+
/** Currently-open thread (null = list view). Lifted so panel + page agree. */
|
|
566
|
+
openThreadId: string | null;
|
|
567
|
+
onOpenThread: (id: string | null) => void;
|
|
568
|
+
/** Highlight-to-comment anchor pending placement on a new thread. */
|
|
569
|
+
pendingAnchor: ThreadAnchor | null;
|
|
570
|
+
onClearAnchor: () => void;
|
|
571
|
+
/** Click handler for anchor pills — should scroll/pulse the anchored section. */
|
|
572
|
+
onAnchorClick: (anchor: ThreadAnchor) => void;
|
|
573
|
+
/** Set of thread ids currently shimmering (no user-supplied title yet). */
|
|
574
|
+
shimmeringThreadIds: Set<string>;
|
|
575
|
+
/** Whether the current user is allowed to post internal-only threads/replies. */
|
|
576
|
+
isInternalUser: boolean;
|
|
577
|
+
/** Submit a brand-new top-level thread. */
|
|
578
|
+
onCreateThread: (data: {
|
|
579
|
+
title: string;
|
|
580
|
+
content: string;
|
|
581
|
+
isInternal: boolean;
|
|
582
|
+
anchor: ThreadAnchor | null;
|
|
583
|
+
attachmentIds: string[];
|
|
584
|
+
}) => Promise<void>;
|
|
585
|
+
/** Submit a reply on an existing thread. */
|
|
586
|
+
onCreateReply: (parentId: string, content: string, isInternal: boolean) => Promise<void>;
|
|
587
|
+
/** Update title or status on an existing thread. */
|
|
588
|
+
onUpdateThread: (threadId: string, body: {
|
|
589
|
+
title?: string;
|
|
590
|
+
thread_status?: ThreadStatus;
|
|
591
|
+
}) => Promise<void>;
|
|
592
|
+
/** Upload a file attachment for the current task. Returns the new row. */
|
|
593
|
+
onUploadAttachment: (file: File) => Promise<Attachment>;
|
|
594
|
+
/** Add a link/recording attachment for the current task. */
|
|
595
|
+
onAddLinkAttachment: (url: string, name?: string) => Promise<Attachment>;
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Right-side panel with Threads / Activity tabs, collapse toggle, thread list,
|
|
599
|
+
* thread detail view, and the new-thread composer. Designed to be `xl:flex
|
|
600
|
+
* hidden` and `fixed` to the right edge by the consuming page.
|
|
601
|
+
*/
|
|
602
|
+
declare function ThreadsPanel({ threads, activity, attachments, open, onToggle, openThreadId, onOpenThread, pendingAnchor, onClearAnchor, onAnchorClick, shimmeringThreadIds, isInternalUser, onCreateThread, onCreateReply, onUpdateThread, onUploadAttachment, onAddLinkAttachment, }: ThreadsPanelProps): react_jsx_runtime.JSX.Element;
|
|
603
|
+
|
|
604
|
+
interface ThreadCardProps {
|
|
605
|
+
thread: Thread;
|
|
606
|
+
/** Open the full thread detail view. */
|
|
607
|
+
onOpen: () => void;
|
|
608
|
+
/** Click handler for an anchor pill — should scroll/pulse the anchored section. */
|
|
609
|
+
onAnchorClick: (anchor: ThreadAnchor) => void;
|
|
610
|
+
/** When true, render the title as a shimmer placeholder ("AI is generating a title"). */
|
|
611
|
+
shimmer?: boolean;
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* One row in the threads list. Click anywhere on the card to open the
|
|
615
|
+
* thread detail view. Anchor pills stop propagation so they can scroll
|
|
616
|
+
* back to the highlighted description section instead.
|
|
617
|
+
*/
|
|
618
|
+
declare function ThreadCard({ thread, onOpen, onAnchorClick, shimmer }: ThreadCardProps): react_jsx_runtime.JSX.Element;
|
|
619
|
+
|
|
620
|
+
interface ThreadDetailViewProps {
|
|
621
|
+
thread: Thread;
|
|
622
|
+
onBack: () => void;
|
|
623
|
+
onReply: (content: string, isInternal: boolean) => Promise<void>;
|
|
624
|
+
onUpdateThread: (body: {
|
|
625
|
+
title?: string;
|
|
626
|
+
thread_status?: ThreadStatus;
|
|
627
|
+
}) => Promise<void>;
|
|
628
|
+
onAnchorClick: (anchor: ThreadAnchor) => void;
|
|
629
|
+
/** Whether the current user can post internal-only replies. */
|
|
630
|
+
isInternalUser: boolean;
|
|
631
|
+
}
|
|
632
|
+
/** Detail view shown inside the threads panel when a thread card is clicked. */
|
|
633
|
+
declare function ThreadDetailView({ thread, onBack, onReply, onUpdateThread, onAnchorClick, isInternalUser, }: ThreadDetailViewProps): react_jsx_runtime.JSX.Element;
|
|
634
|
+
|
|
635
|
+
interface ThreadComposerProps {
|
|
636
|
+
/** Existing attachments on the task — used to render chips for already-uploaded files. */
|
|
637
|
+
attachments: Attachment[];
|
|
638
|
+
/** Anchor to attach when posting (set by highlight-to-comment). */
|
|
639
|
+
pendingAnchor: ThreadAnchor | null;
|
|
640
|
+
/** Clear the pending anchor (e.g. user dismisses the highlight). */
|
|
641
|
+
onClearAnchor: () => void;
|
|
642
|
+
/** Submit the new thread. */
|
|
643
|
+
onSubmit: (data: {
|
|
644
|
+
title: string;
|
|
645
|
+
content: string;
|
|
646
|
+
isInternal: boolean;
|
|
647
|
+
anchor: ThreadAnchor | null;
|
|
648
|
+
attachmentIds: string[];
|
|
649
|
+
}) => Promise<void>;
|
|
650
|
+
/** Upload a file as an attachment to this task. Returns the new attachment row. */
|
|
651
|
+
onUpload: (file: File) => Promise<Attachment>;
|
|
652
|
+
/** Add a link/recording as an attachment. Returns the new attachment row. */
|
|
653
|
+
onAddLink: (url: string, name?: string) => Promise<Attachment>;
|
|
654
|
+
/** Whether the current user is allowed to post internal-only threads. */
|
|
655
|
+
isInternalUser: boolean;
|
|
656
|
+
/** When false, render the collapsed "Start a thread" button instead. */
|
|
657
|
+
open: boolean;
|
|
658
|
+
/** Open the composer (e.g. user clicks the start button). */
|
|
659
|
+
onOpen: () => void;
|
|
660
|
+
/** Close + reset the composer. */
|
|
661
|
+
onClose: () => void;
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Composer for new threads. Renders a single button when collapsed; when
|
|
665
|
+
* open, renders an optional anchor strip, optional title input, body
|
|
666
|
+
* textarea with @mentions, attachment buttons + chips, internal toggle,
|
|
667
|
+
* and the Post button.
|
|
668
|
+
*/
|
|
669
|
+
declare function ThreadComposer({ attachments, pendingAnchor, onClearAnchor, onSubmit, onUpload, onAddLink, isInternalUser, open, onOpen, onClose, }: ThreadComposerProps): react_jsx_runtime.JSX.Element;
|
|
670
|
+
|
|
671
|
+
interface ActivityListProps {
|
|
672
|
+
activity: ActivityEntry[];
|
|
673
|
+
}
|
|
674
|
+
/** Status-change activity log shown in the Activity tab of the threads panel. */
|
|
675
|
+
declare function ActivityList({ activity }: ActivityListProps): react_jsx_runtime.JSX.Element;
|
|
676
|
+
|
|
677
|
+
type ContextPillVariant = 'amber' | 'purple' | 'blue' | 'emerald' | 'neutral';
|
|
678
|
+
interface ContextPillProps {
|
|
679
|
+
variant: ContextPillVariant;
|
|
680
|
+
icon: React__default.ComponentType<{
|
|
681
|
+
className?: string;
|
|
682
|
+
strokeWidth?: number;
|
|
683
|
+
size?: number;
|
|
684
|
+
}>;
|
|
685
|
+
label: string;
|
|
686
|
+
onClick?: (e: React__default.MouseEvent) => void;
|
|
687
|
+
}
|
|
688
|
+
/** A small color-coded pill used on thread cards (highlights, attachments, etc.). */
|
|
689
|
+
declare function ContextPill({ variant, icon: IconC, label, onClick }: ContextPillProps): react_jsx_runtime.JSX.Element;
|
|
690
|
+
|
|
691
|
+
interface BubbleState {
|
|
692
|
+
/** X coordinate (page-relative, viewport center of the selection rect). */
|
|
693
|
+
x: number;
|
|
694
|
+
/** Y coordinate (page-relative, scroll-aware). */
|
|
695
|
+
y: number;
|
|
696
|
+
section: SectionKey;
|
|
697
|
+
snippet: string;
|
|
698
|
+
}
|
|
699
|
+
interface UseHighlightAnchorResult {
|
|
700
|
+
/** Transient bubble state — set when the user has selected text inside a [data-section] block. */
|
|
701
|
+
bubble: BubbleState | null;
|
|
702
|
+
/** Manually clear the bubble (e.g. after user clicks the Comment button). */
|
|
703
|
+
clearBubble: () => void;
|
|
704
|
+
/** The anchor pending placement on a new thread, or null. */
|
|
705
|
+
pendingAnchor: ThreadAnchor | null;
|
|
706
|
+
/** Promote the current bubble to a pendingAnchor (the new-thread composer should react to this). */
|
|
707
|
+
beginAnchoredThread: () => ThreadAnchor | null;
|
|
708
|
+
/** Clear `pendingAnchor` after the thread is posted or the composer is dismissed. */
|
|
709
|
+
clearPendingAnchor: () => void;
|
|
710
|
+
/** Scroll the anchored section into view and pulse it briefly. Pass to ThreadCard / ThreadDetailView. */
|
|
711
|
+
focusAnchor: (anchor: ThreadAnchor) => void;
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Wires up highlight-to-comment selection behaviour. The consuming page
|
|
715
|
+
* needs to render the description sections with `data-section={sectionKey}`
|
|
716
|
+
* for the bubble + focusAnchor to find the right element. Pair this hook
|
|
717
|
+
* with `<HighlightBubble bubble={bubble} onComment={beginAnchoredThread} />`
|
|
718
|
+
* and pass `pendingAnchor` / `clearPendingAnchor` to `<ThreadsPanel>`.
|
|
719
|
+
*
|
|
720
|
+
* Bug #1 fix lives in `<HighlightBubble>`: the button uses
|
|
721
|
+
* onMouseDown.preventDefault so clicking it doesn't blow away the selection.
|
|
722
|
+
*/
|
|
723
|
+
declare function useHighlightAnchor(): UseHighlightAnchorResult;
|
|
724
|
+
|
|
725
|
+
interface HighlightBubbleProps {
|
|
726
|
+
bubble: BubbleState | null;
|
|
727
|
+
/** Called when the user clicks the bubble to start a new anchored thread. */
|
|
728
|
+
onComment: () => void;
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* Floating "Comment" button shown above a text selection inside a
|
|
732
|
+
* `[data-section]` block. The page should render this once at top level
|
|
733
|
+
* and pass `bubble` / `onComment` from `useHighlightAnchor`.
|
|
734
|
+
*
|
|
735
|
+
* Bug #1 fix: onMouseDown.preventDefault keeps focus on the selection so
|
|
736
|
+
* the snippet survives until onClick reads it.
|
|
737
|
+
*/
|
|
738
|
+
declare function HighlightBubble({ bubble, onComment }: HighlightBubbleProps): react_jsx_runtime.JSX.Element | null;
|
|
739
|
+
|
|
287
740
|
interface TaskBoardService {
|
|
288
741
|
listTasks(projectSlug: string, perColumn?: number): Promise<Record<string, ColumnResponse>>;
|
|
289
742
|
listColumnTasks(projectSlug: string, statusKey: string, offset: number, limit: number): Promise<Task[]>;
|
|
@@ -296,6 +749,20 @@ interface TaskBoardService {
|
|
|
296
749
|
addComment(taskId: string, data: CreateCommentPayload): Promise<Comment>;
|
|
297
750
|
editComment(taskId: string, commentId: string, data: EditCommentPayload): Promise<Comment>;
|
|
298
751
|
deleteComment(taskId: string, commentId: string): Promise<void>;
|
|
752
|
+
/** Update a top-level comment's title and/or thread_status. */
|
|
753
|
+
updateThread(taskId: string, threadId: string, data: UpdateThreadPayload): Promise<Comment>;
|
|
754
|
+
listQuestions(taskId: string): Promise<Question[]>;
|
|
755
|
+
createQuestion(taskId: string, data: CreateQuestionPayload): Promise<Question>;
|
|
756
|
+
updateQuestion(taskId: string, questionId: string, data: UpdateQuestionPayload): Promise<Question>;
|
|
757
|
+
deleteQuestion(taskId: string, questionId: string): Promise<void>;
|
|
758
|
+
addQuestionReply(taskId: string, questionId: string, data: CreateQuestionReplyPayload): Promise<{
|
|
759
|
+
id: string;
|
|
760
|
+
}>;
|
|
761
|
+
deleteQuestionReply(taskId: string, questionId: string, replyId: string): Promise<void>;
|
|
762
|
+
listAttachments(taskId: string): Promise<Attachment[]>;
|
|
763
|
+
uploadAttachment(taskId: string, file: File): Promise<Attachment>;
|
|
764
|
+
addLinkAttachment(taskId: string, data: CreateLinkAttachmentPayload): Promise<Attachment>;
|
|
765
|
+
deleteAttachment(taskId: string, attachmentId: string): Promise<void>;
|
|
299
766
|
listProjects(): Promise<Project[]>;
|
|
300
767
|
searchMentionUsers(query: string): Promise<MentionUser[]>;
|
|
301
768
|
getNotificationCount(): Promise<number>;
|
|
@@ -320,6 +787,8 @@ interface TaskBoardConfig {
|
|
|
320
787
|
tags?: TagConfig[];
|
|
321
788
|
/** Base API path (defaults to '/api/v1/taskboard') */
|
|
322
789
|
apiBasePath?: string;
|
|
790
|
+
/** Label shown on internal-only comment chips. Defaults to "Internal". */
|
|
791
|
+
internalLabel?: string;
|
|
323
792
|
/** Callbacks */
|
|
324
793
|
onTaskCreate?: (task: Task) => void;
|
|
325
794
|
onTaskUpdate?: (task: Task) => void;
|
|
@@ -346,6 +815,7 @@ interface TaskBoardContextValue {
|
|
|
346
815
|
columns: ColumnConfig[];
|
|
347
816
|
priorities: PriorityConfig[];
|
|
348
817
|
tags: TagConfig[];
|
|
818
|
+
internalLabel: string;
|
|
349
819
|
config: TaskBoardConfig;
|
|
350
820
|
features: Required<NonNullable<TaskBoardConfig['features']>>;
|
|
351
821
|
}
|
|
@@ -399,11 +869,60 @@ declare function useShareLink(): {
|
|
|
399
869
|
copyShareLink: (taskId: string, projectSlug: string) => void;
|
|
400
870
|
};
|
|
401
871
|
|
|
872
|
+
interface UseTaskQuestionsResult {
|
|
873
|
+
questions: Question[];
|
|
874
|
+
loading: boolean;
|
|
875
|
+
/** Re-fetch from the server. Use after mutations to refresh state. */
|
|
876
|
+
refresh: () => Promise<void>;
|
|
877
|
+
/** Optimistic local replace (e.g. when the parent page re-fetches the whole task). */
|
|
878
|
+
setQuestions: (next: Question[]) => void;
|
|
879
|
+
createQuestion: (text: string) => Promise<void>;
|
|
880
|
+
updateQuestion: (questionId: string, text: string) => Promise<void>;
|
|
881
|
+
setStatus: (questionId: string, status: QuestionStatus) => Promise<void>;
|
|
882
|
+
deleteQuestion: (questionId: string) => Promise<void>;
|
|
883
|
+
addReply: (questionId: string, content: string) => Promise<void>;
|
|
884
|
+
deleteReply: (questionId: string, replyId: string) => Promise<void>;
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* Manage the structured Outstanding Questions list for a single task.
|
|
888
|
+
*
|
|
889
|
+
* The hook owns its own loading state and re-fetches after each mutation
|
|
890
|
+
* so the parent component can stay simple. Pass `initial` from the
|
|
891
|
+
* embedded `TaskDetailResponse.questions` to avoid an extra request on
|
|
892
|
+
* mount.
|
|
893
|
+
*/
|
|
894
|
+
declare function useTaskQuestions(taskId: string | null, initial?: Question[]): UseTaskQuestionsResult;
|
|
895
|
+
|
|
896
|
+
interface UseTaskAttachmentsResult {
|
|
897
|
+
attachments: Attachment[];
|
|
898
|
+
loading: boolean;
|
|
899
|
+
/** Re-fetch from the server. */
|
|
900
|
+
refresh: () => Promise<void>;
|
|
901
|
+
setAttachments: (next: Attachment[]) => void;
|
|
902
|
+
/** Upload a single file (image or document). Returns the new attachment. */
|
|
903
|
+
uploadFile: (file: File) => Promise<Attachment>;
|
|
904
|
+
/** Add an external link / recording. Returns the new attachment. */
|
|
905
|
+
addLink: (payload: CreateLinkAttachmentPayload) => Promise<Attachment>;
|
|
906
|
+
/** Delete an attachment (and its underlying GCS object, if any). */
|
|
907
|
+
remove: (attachmentId: string) => Promise<void>;
|
|
908
|
+
}
|
|
909
|
+
/**
|
|
910
|
+
* Manage the attachments collection for a single task.
|
|
911
|
+
*
|
|
912
|
+
* Pass `initial` from the embedded `TaskDetailResponse.attachments` to
|
|
913
|
+
* avoid an extra request on mount.
|
|
914
|
+
*/
|
|
915
|
+
declare function useTaskAttachments(taskId: string | null, initial?: Attachment[]): UseTaskAttachmentsResult;
|
|
916
|
+
|
|
402
917
|
declare function getPriorityStyle(priority: string): PriorityConfig;
|
|
403
918
|
declare function getTagStyle(tag: string): TagConfig;
|
|
404
919
|
declare function getInitials(name: string): string;
|
|
920
|
+
/** Parse a date string, treating timezone-naive Mongo timestamps as UTC. */
|
|
921
|
+
declare function parseDate(dateStr: string): Date;
|
|
405
922
|
declare function formatDate(dateStr: string): string;
|
|
406
923
|
declare function formatDateTime(dateStr: string): string;
|
|
924
|
+
/** Build a short task ID label like "T-AB12CD" from a Mongo ObjectId. */
|
|
925
|
+
declare function formatTaskId(id: string): string;
|
|
407
926
|
declare function getDescriptionPreview(desc: StructuredDescription | string | undefined): string;
|
|
408
927
|
declare function hasDescription(desc: StructuredDescription | string | undefined): boolean;
|
|
409
928
|
declare function getUserProjects(apps: string[], allProjects: {
|
|
@@ -414,6 +933,20 @@ declare function getUserProjects(apps: string[], allProjects: {
|
|
|
414
933
|
name: string;
|
|
415
934
|
}[];
|
|
416
935
|
|
|
936
|
+
/**
|
|
937
|
+
* Markdown ⇄ HTML round-trip helpers used by the WYSIWYG `MarkdownEditor`.
|
|
938
|
+
*
|
|
939
|
+
* Storage format: markdown (so existing readers, the backend, and previews
|
|
940
|
+
* keep working). Editing format: HTML in a contenteditable div, so users
|
|
941
|
+
* never see raw `**` / `- ` / `# ` syntax.
|
|
942
|
+
*
|
|
943
|
+
* Mentions stored as `@[Name](username)` round-trip through the
|
|
944
|
+
* `<span class="mention-pill" data-mention-username="...">@Name</span>`
|
|
945
|
+
* representation.
|
|
946
|
+
*/
|
|
947
|
+
declare function mdToHtml(md: string): string;
|
|
948
|
+
declare function htmlToMd(html: string): string;
|
|
949
|
+
|
|
417
950
|
declare const DEFAULT_COLUMNS: ColumnConfig[];
|
|
418
951
|
declare const DEFAULT_PRIORITIES: PriorityConfig[];
|
|
419
952
|
declare const PREDEFINED_TAGS: TagConfig[];
|
|
@@ -421,6 +954,8 @@ declare const DESCRIPTION_SECTIONS: DescriptionSectionConfig[];
|
|
|
421
954
|
declare const EMPTY_DESCRIPTION: StructuredDescription;
|
|
422
955
|
declare const POSITION_GAP = 1000;
|
|
423
956
|
declare const DEFAULT_PAGE_SIZE = 10;
|
|
957
|
+
/** Default label shown on internal-only comments. Override via TaskBoardProvider's `internalLabel` prop. */
|
|
958
|
+
declare const DEFAULT_INTERNAL_LABEL = "Internal";
|
|
424
959
|
|
|
425
960
|
interface IconProps {
|
|
426
961
|
className?: string;
|
|
@@ -430,15 +965,39 @@ interface IconProps {
|
|
|
430
965
|
declare const PlusIcon: React__default.FC<IconProps>;
|
|
431
966
|
declare const XIcon: React__default.FC<IconProps>;
|
|
432
967
|
declare const ChevronDownIcon: React__default.FC<IconProps>;
|
|
968
|
+
declare const ChevronLeftIcon: React__default.FC<IconProps>;
|
|
969
|
+
declare const ChevronRightIcon: React__default.FC<IconProps>;
|
|
970
|
+
declare const ArrowLeftIcon: React__default.FC<IconProps>;
|
|
971
|
+
declare const MoreVerticalIcon: React__default.FC<IconProps>;
|
|
972
|
+
declare const Share2Icon: React__default.FC<IconProps>;
|
|
433
973
|
declare const MessageSquareIcon: React__default.FC<IconProps>;
|
|
434
974
|
declare const KanbanIcon: React__default.FC<IconProps>;
|
|
435
975
|
declare const LinkIcon: React__default.FC<IconProps>;
|
|
976
|
+
declare const Link2Icon: React__default.FC<IconProps>;
|
|
977
|
+
declare const ExternalLinkIcon: React__default.FC<IconProps>;
|
|
978
|
+
declare const ImageIcon: React__default.FC<IconProps>;
|
|
979
|
+
declare const FileTextIcon: React__default.FC<IconProps>;
|
|
436
980
|
declare const CheckIcon: React__default.FC<IconProps>;
|
|
981
|
+
declare const CheckCircle2Icon: React__default.FC<IconProps>;
|
|
437
982
|
declare const BellIcon: React__default.FC<IconProps>;
|
|
438
983
|
declare const FilterIcon: React__default.FC<IconProps>;
|
|
439
984
|
declare const PencilIcon: React__default.FC<IconProps>;
|
|
440
985
|
declare const TrashIcon: React__default.FC<IconProps>;
|
|
441
986
|
declare const LockIcon: React__default.FC<IconProps>;
|
|
442
987
|
declare const FeedbackIcon: React__default.FC<IconProps>;
|
|
988
|
+
declare const HelpCircleIcon: React__default.FC<IconProps>;
|
|
989
|
+
declare const CornerUpLeftIcon: React__default.FC<IconProps>;
|
|
990
|
+
declare const RotateCcwIcon: React__default.FC<IconProps>;
|
|
991
|
+
declare const Bold: React__default.FC<IconProps>;
|
|
992
|
+
declare const Italic: React__default.FC<IconProps>;
|
|
993
|
+
declare const List: React__default.FC<IconProps>;
|
|
994
|
+
declare const ListOrdered: React__default.FC<IconProps>;
|
|
995
|
+
declare const Heading2: React__default.FC<IconProps>;
|
|
996
|
+
declare const Quote: React__default.FC<IconProps>;
|
|
997
|
+
declare const Code: React__default.FC<IconProps>;
|
|
998
|
+
declare const ChatDotsIcon: React__default.FC<IconProps>;
|
|
999
|
+
declare const HistoryIcon: React__default.FC<IconProps>;
|
|
1000
|
+
/** Sidebar-toggle icon used to collapse / re-open the threads panel. */
|
|
1001
|
+
declare const SidebarToggleIcon: React__default.FC<IconProps>;
|
|
443
1002
|
|
|
444
|
-
export { type ActivityEntry, type ApiClient, type ApiClientConfig, BellIcon, BoardSkeleton, CheckIcon, ChevronDownIcon, type ColumnConfig, type ColumnResponse, type ColumnTotals, type ColumnUnreads, type Comment, type CreateCommentPayload, CreateTaskModal, type CreateTaskModalProps, type CreateTaskPayload, DEFAULT_COLUMNS, DEFAULT_PAGE_SIZE, DEFAULT_PRIORITIES, DESCRIPTION_SECTIONS, type DescriptionSectionConfig, EMPTY_DESCRIPTION, type EditCommentPayload, FeedbackIcon, FilterBar, type FilterBarProps, FilterIcon, KanbanColumn, type KanbanColumnProps, KanbanIcon, LinkIcon, LockIcon, MentionText, MentionTextarea, type MentionTextareaProps, type MentionUser, MessageSquareIcon, type Notification, NotificationBell, type NotificationBellProps, type NotificationCountResponse, POSITION_GAP, PREDEFINED_TAGS, PencilIcon, PlusIcon, PriorityBadge, type PriorityBadgeProps, type PriorityConfig, type Project, SkeletonCard, SkeletonPulse, type StructuredDescription, TagBadge, type TagBadgeProps, type TagConfig, type Task, TaskBoard, type TaskBoardConfig, type TaskBoardContextValue, type TaskBoardProps, TaskBoardProvider, type TaskBoardService, type TaskBoardUser, TaskCard, type TaskCardProps, TaskDetailPanel, type TaskDetailPanelProps, type TaskDetailResponse, type TasksByStatus, TrashIcon, type UpdateTaskPayload, UserAvatar, type UserAvatarProps, XIcon, createTaskBoardService, formatDate, formatDateTime, getDescriptionPreview, getInitials, getPriorityStyle, getTagStyle, getUserProjects, hasDescription, toDisplayText, toStoredText, useShareLink, useTaskActions, useTaskBoard, useTaskBoardContext, useTaskDetail };
|
|
1003
|
+
export { type ActivityEntry, ActivityList, type ActivityListProps, type ApiClient, type ApiClientConfig, ArrowLeftIcon, type Attachment, type AttachmentKind, AttachmentsSection, type AttachmentsSectionProps, BellIcon, BoardSkeleton, Bold, type BubbleState, ChatDotsIcon, CheckCircle2Icon, CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, Code, type ColumnConfig, type ColumnResponse, type ColumnTotals, type ColumnUnreads, type Comment, ContextPill, type ContextPillProps, type ContextPillVariant, CornerUpLeftIcon, type CreateCommentPayload, type CreateLinkAttachmentPayload, type CreateQuestionPayload, type CreateQuestionReplyPayload, CreateTaskModal, type CreateTaskModalProps, type CreateTaskPayload, DEFAULT_COLUMNS, DEFAULT_INTERNAL_LABEL, DEFAULT_PAGE_SIZE, DEFAULT_PRIORITIES, DESCRIPTION_SECTIONS, DescriptionSection, type DescriptionSectionConfig, type DescriptionSectionProps, EMPTY_DESCRIPTION, type EditCommentPayload, ExternalLinkIcon, FeedbackIcon, FileTextIcon, FilterBar, type FilterBarProps, FilterIcon, Heading2, HelpCircleIcon, HighlightBubble, type HighlightBubbleProps, HistoryIcon, ImageIcon, Italic, KanbanColumn, type KanbanColumnProps, KanbanIcon, Link2Icon, LinkIcon, List, ListOrdered, LockIcon, MarkdownEditor, type MarkdownEditorProps, MarkdownView, MentionText, MentionTextarea, type MentionTextareaProps, type MentionUser, MessageSquareIcon, MoreVerticalIcon, type Notification, NotificationBell, type NotificationBellProps, type NotificationCountResponse, OutstandingQuestionsSection, type OutstandingQuestionsSectionProps, POSITION_GAP, PREDEFINED_TAGS, PencilIcon, PlusIcon, PriorityBadge, type PriorityBadgeProps, type PriorityConfig, type Project, type Question, type QuestionReply, type QuestionStatus, Quote, RotateCcwIcon, type SectionKey, type SectionStatus, Share2Icon, SidebarToggleIcon, SkeletonCard, SkeletonPulse, type StructuredDescription, TagBadge, type TagBadgeProps, type TagConfig, type Task, TaskBoard, type TaskBoardConfig, type TaskBoardContextValue, type TaskBoardProps, TaskBoardProvider, type TaskBoardService, type TaskBoardUser, TaskCard, type TaskCardProps, TaskDetailPanel, type TaskDetailPanelProps, type TaskDetailResponse, TaskDetailView, type TaskDetailViewProps, type TasksByStatus, type Thread, type ThreadAnchor, ThreadCard, type ThreadCardProps, ThreadComposer, type ThreadComposerProps, ThreadDetailView, type ThreadDetailViewProps, type ThreadReply, type ThreadStatus, ThreadsPanel, type ThreadsPanelProps, TrashIcon, type UpdateQuestionPayload, type UpdateTaskPayload, type UpdateThreadPayload, type UseHighlightAnchorResult, type UseTaskAttachmentsResult, type UseTaskQuestionsResult, UserAvatar, type UserAvatarProps, XIcon, createTaskBoardService, deriveThreads, formatDate, formatDateTime, formatTaskId, getDescriptionPreview, getInitials, getPriorityStyle, getTagStyle, getUserProjects, hasDescription, htmlToMd, mdToHtml, parseDate, sectionLabel, timeAgo, toDisplayText, toStoredText, useHighlightAnchor, useShareLink, useTaskActions, useTaskAttachments, useTaskBoard, useTaskBoardContext, useTaskDetail, useTaskQuestions };
|