@lovett/ui 0.0.11 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/dist/chunk-RBYWGBQ2.js +2752 -0
  2. package/dist/chunk-RBYWGBQ2.js.map +1 -0
  3. package/dist/index.d.ts +5574 -57
  4. package/dist/index.js +21650 -11206
  5. package/dist/index.js.map +1 -1
  6. package/dist/rich-composer-impl-5NO443A6.js +1859 -0
  7. package/dist/rich-composer-impl-5NO443A6.js.map +1 -0
  8. package/dist/styles.css +1570 -0
  9. package/dist/tokens.css +112 -0
  10. package/package.json +8 -1
  11. package/src/__tests__/avatar.test.tsx +272 -0
  12. package/src/__tests__/bar-chart.test.tsx +809 -0
  13. package/src/__tests__/board.test.tsx +420 -0
  14. package/src/__tests__/chart-math.test.ts +922 -0
  15. package/src/__tests__/chart-series.test.ts +339 -0
  16. package/src/__tests__/code-block.test.tsx +134 -0
  17. package/src/__tests__/display-popover.test.tsx +195 -0
  18. package/src/__tests__/display-store.test.tsx +307 -0
  19. package/src/__tests__/donut-chart.test.tsx +397 -0
  20. package/src/__tests__/dropdown-menu.test.tsx +156 -2
  21. package/src/__tests__/filter-menu.test.tsx +175 -0
  22. package/src/__tests__/gauge-ring.test.tsx +233 -0
  23. package/src/__tests__/line-chart.test.tsx +612 -0
  24. package/src/__tests__/ranked-bars.test.tsx +343 -0
  25. package/src/__tests__/remark-underline.test.ts +194 -0
  26. package/src/__tests__/sortable.test.tsx +394 -0
  27. package/src/__tests__/sparkline.test.tsx +368 -0
  28. package/src/__tests__/stat-layer.test.tsx +271 -0
  29. package/src/__tests__/stat-strip.test.tsx +175 -0
  30. package/src/__tests__/status.test.tsx +293 -0
  31. package/src/__tests__/tabs.test.tsx +303 -0
  32. package/src/__tests__/token-shape.test.ts +132 -2
  33. package/src/avatar.tsx +352 -0
  34. package/src/bar-chart.tsx +1214 -0
  35. package/src/board.tsx +658 -0
  36. package/src/chart-frame.tsx +960 -0
  37. package/src/chart-legend.tsx +304 -0
  38. package/src/chart-tooltip.tsx +267 -0
  39. package/src/code-block.tsx +62 -8
  40. package/src/delta-chip.tsx +263 -0
  41. package/src/detail/__tests__/activity-pane.test.tsx +369 -0
  42. package/src/detail/__tests__/detail-chrome.test.tsx +134 -0
  43. package/src/detail/__tests__/detail-surface.test.tsx +529 -0
  44. package/src/detail/__tests__/field-row.test.tsx +357 -0
  45. package/src/detail/activity-pane.tsx +611 -0
  46. package/src/detail/calendar.tsx +355 -0
  47. package/src/detail/detail-divider.tsx +261 -0
  48. package/src/detail/detail-header.tsx +287 -0
  49. package/src/detail/detail-menu.tsx +254 -0
  50. package/src/detail/detail-surface.tsx +1110 -0
  51. package/src/detail/field-list.tsx +196 -0
  52. package/src/detail/field-row.tsx +1131 -0
  53. package/src/detail/index.ts +58 -0
  54. package/src/detail/segmented-choice.tsx +94 -0
  55. package/src/detail/types.ts +129 -0
  56. package/src/display-popover.tsx +487 -0
  57. package/src/display-store.tsx +301 -0
  58. package/src/donut-chart.tsx +988 -0
  59. package/src/dropdown-menu.tsx +290 -19
  60. package/src/filter-core/EXPORTS.md +85 -0
  61. package/src/filter-core/__tests__/columns.test.ts +159 -0
  62. package/src/filter-core/__tests__/faceting.test.ts +193 -0
  63. package/src/filter-core/__tests__/filter-fns.test.ts +519 -0
  64. package/src/filter-core/__tests__/operators.test.ts +235 -0
  65. package/src/filter-core/__tests__/state.test.ts +268 -0
  66. package/src/filter-core/__tests__/url.test.ts +350 -0
  67. package/src/filter-core/columns.ts +134 -0
  68. package/src/filter-core/date-utils.ts +38 -0
  69. package/src/filter-core/examples/task-filter-columns.ts +121 -0
  70. package/src/filter-core/faceting.ts +120 -0
  71. package/src/filter-core/filter-fns.ts +335 -0
  72. package/src/filter-core/index.ts +105 -0
  73. package/src/filter-core/operators.ts +433 -0
  74. package/src/filter-core/state.ts +280 -0
  75. package/src/filter-core/types.ts +247 -0
  76. package/src/filter-core/url.ts +261 -0
  77. package/src/filter-dropdown.tsx +12 -0
  78. package/src/filter-menu.tsx +649 -0
  79. package/src/floating-drawer.tsx +19 -1
  80. package/src/gauge-ring.tsx +435 -0
  81. package/src/hue.ts +52 -0
  82. package/src/index.ts +303 -0
  83. package/src/kbd.tsx +27 -4
  84. package/src/lib/chart.ts +866 -0
  85. package/src/lib/focus.ts +43 -1
  86. package/src/lib/layer-stack.ts +30 -3
  87. package/src/lib/remark-underline.ts +443 -0
  88. package/src/lib/series.ts +169 -0
  89. package/src/line-chart.tsx +1176 -0
  90. package/src/markdown.tsx +26 -7
  91. package/src/modal.tsx +42 -18
  92. package/src/progress-ledger.tsx +304 -0
  93. package/src/ranked-bars.tsx +386 -0
  94. package/src/segmented-pill.tsx +32 -9
  95. package/src/sortable.tsx +520 -1
  96. package/src/sparkline.tsx +416 -0
  97. package/src/stat-card.tsx +376 -0
  98. package/src/stat-strip.tsx +327 -0
  99. package/src/status.tsx +215 -0
  100. package/src/styles.css +1570 -0
  101. package/src/tabs.tsx +206 -25
  102. package/src/task-card.tsx +610 -0
  103. package/src/thread/__tests__/comment-body-hostile.test.tsx +331 -0
  104. package/src/thread/__tests__/comment-tree.test.ts +151 -0
  105. package/src/thread/__tests__/emoji.test.ts +187 -0
  106. package/src/thread/__tests__/fixtures/thread-fixture.ts +252 -0
  107. package/src/thread/__tests__/link-preview-source.test.ts +120 -0
  108. package/src/thread/__tests__/link-preview.test.tsx +600 -0
  109. package/src/thread/__tests__/markdown-format.test.ts +82 -0
  110. package/src/thread/__tests__/markdown-spec.test.ts +469 -0
  111. package/src/thread/__tests__/relative-time.test.ts +71 -0
  112. package/src/thread/__tests__/rich-composer.test.tsx +799 -0
  113. package/src/thread/__tests__/scroll-caret.test.ts +58 -0
  114. package/src/thread/__tests__/suggestion-list.test.tsx +648 -0
  115. package/src/thread/__tests__/thread-scroll-ownership.test.tsx +88 -0
  116. package/src/thread/__tests__/thread.test.tsx +742 -0
  117. package/src/thread/__tests__/use-attachments.test.tsx +679 -0
  118. package/src/thread/actions.tsx +196 -0
  119. package/src/thread/attachments.tsx +1071 -0
  120. package/src/thread/comment-body.tsx +148 -0
  121. package/src/thread/comment-tree.ts +182 -0
  122. package/src/thread/comment.tsx +967 -0
  123. package/src/thread/composer-footer.tsx +125 -0
  124. package/src/thread/composer.tsx +319 -0
  125. package/src/thread/emoji.ts +283 -0
  126. package/src/thread/index.ts +153 -0
  127. package/src/thread/link-preview.tsx +341 -0
  128. package/src/thread/markdown-format.ts +155 -0
  129. package/src/thread/markdown-spec.ts +754 -0
  130. package/src/thread/rail.tsx +372 -0
  131. package/src/thread/reactions.tsx +415 -0
  132. package/src/thread/relative-time.tsx +94 -0
  133. package/src/thread/rich-composer-impl.tsx +1601 -0
  134. package/src/thread/rich-composer.tsx +195 -0
  135. package/src/thread/scroll-caret.ts +37 -0
  136. package/src/thread/suggestion-list.tsx +182 -0
  137. package/src/thread/thread.tsx +718 -0
  138. package/src/thread/types.ts +232 -0
  139. package/src/thread/use-attachments.ts +598 -0
  140. package/src/thread/use-now.ts +73 -0
  141. package/src/thread/use-thread.ts +316 -0
  142. package/src/tokens.css +112 -0
@@ -0,0 +1,232 @@
1
+ /**
2
+ * Thread primitive — the data shapes.
3
+ *
4
+ * ADR-147 T3. This module is deliberately entity-agnostic: it knows nothing
5
+ * about tasks, boards, posts, brands, or the wire. It takes already-shaped
6
+ * data and reports intent through callbacks. The `entity_type` registry that
7
+ * makes threads safe (ADR-147 D2) lives server-side; nothing here is an
8
+ * authorization boundary and nothing here should ever be treated as one.
9
+ *
10
+ * Timestamps are integer epoch MILLISECONDS. The D1 schema stores epoch
11
+ * seconds (ADR-147 D16); converting at the API seam is T4's job, not this
12
+ * layer's — a component that guesses at units is a component that renders
13
+ * "56 years ago" the first time someone changes the column.
14
+ */
15
+
16
+ import type { MentionTargetType } from './comment-body'
17
+
18
+ /**
19
+ * ADR-147 D8 — reactions are a bounded set of glyph KEYS, never emoji and
20
+ * never a free string. The bound is what makes the column enumerable, the
21
+ * rendering consistent across platforms, and the no-emoji rule enforceable.
22
+ */
23
+ export const THREAD_REACTION_KEYS = [
24
+ 'up',
25
+ 'heart',
26
+ 'celebrate',
27
+ 'eyes',
28
+ 'check',
29
+ 'question',
30
+ ] as const
31
+
32
+ export type ThreadReactionKey = (typeof THREAD_REACTION_KEYS)[number]
33
+
34
+ /** One reaction row as the API groups it: key, tally, and the viewer's own state. */
35
+ export interface ThreadReactionCount {
36
+ readonly key: ThreadReactionKey
37
+ readonly count: number
38
+ readonly viewerReacted: boolean
39
+ }
40
+
41
+ /**
42
+ * ADR-147 D17 — optimistic send is a first-class state, not a nicety.
43
+ * A comment silently lost to a bad connection is the worst thing this surface
44
+ * can do to someone, and it is the most likely.
45
+ */
46
+ export type CommentDeliveryState = 'sent' | 'pending' | 'failed'
47
+
48
+ /**
49
+ * ADR-147 D5 — every field here can be absent. `users.name` / `given_name` /
50
+ * `family_name` are nullable by standing policy (do not fabricate a display
51
+ * name), `author_id` is ON DELETE SET NULL, and no avatar column is populated
52
+ * yet. The display fallback lives in the component (D6), never in the data.
53
+ */
54
+ export interface ThreadAuthor {
55
+ readonly id?: string | null | undefined
56
+ readonly name?: string | null | undefined
57
+ /** Used as the display fallback when `name` is absent — D5's "only an email" case. */
58
+ readonly email?: string | null | undefined
59
+ /** May 404 or be blocked. The avatar keeps its initial behind the photo (D14). */
60
+ readonly avatarUrl?: string | null | undefined
61
+ /** Identity colour as DATA (D6). Omitted falls back to Avatar's documented hash. */
62
+ readonly tint?: string | null | undefined
63
+ /** A short role chip beside the name, e.g. "Client". */
64
+ readonly role?: string | null | undefined
65
+ }
66
+
67
+ /**
68
+ * `gif` joined `image` and `file` with ADR-151 D4.
69
+ *
70
+ * It is its own kind rather than a flavour of `image` because it identifies a
71
+ * Giphy reference whose stored rendition is an mp4. That is what lets its
72
+ * renderer genuinely pause on the first frame. Uploaded GIFs deliberately stay
73
+ * `image` and are distinguished by `mime: 'image/gif'`; their renderer swaps a
74
+ * captured canvas still for the live image under reduced motion.
75
+ */
76
+ export type ThreadAttachmentKind = 'image' | 'gif' | 'file'
77
+
78
+ /**
79
+ * ADR-147 D9. `aspectRatio` is captured at attach time so the thread reserves
80
+ * the image slot before it loads — the layout-stability rule. An attachment
81
+ * without one gets a declared 16/10 box rather than a collapsing one.
82
+ */
83
+ export interface ThreadAttachment {
84
+ readonly id: string
85
+ readonly kind: ThreadAttachmentKind
86
+ readonly name: string
87
+ readonly url?: string | undefined
88
+ readonly mime?: string | undefined
89
+ readonly sizeBytes?: number | undefined
90
+ readonly aspectRatio?: number | undefined
91
+ }
92
+
93
+ /**
94
+ * One unfurled link card on one comment (ADR-150 D8).
95
+ *
96
+ * This is the WIRE shape, and it is deliberately narrower than the cache row
97
+ * behind it. `thread_link_previews` also carries `status` (`ok` / `failed` /
98
+ * `blocked`) and internal failure codes; a thread read returns only `ok`,
99
+ * unhidden rows, so neither ever crosses the wire. A client cannot tell a
100
+ * blocked URL from one nobody has fetched yet, which is the intent.
101
+ *
102
+ * `title` is NOT nullable, unlike everything else here: D5 makes a non-empty
103
+ * title the condition for writing an `ok` row at all, so a card without one
104
+ * does not exist. `domain` is derived by the WORKER from the URL that was
105
+ * actually fetched — never from `og:site_name`, which the target page chooses.
106
+ *
107
+ * `href` is per-OCCURRENCE and keeps its fragment, so clicking the card lands
108
+ * on the anchor the person meant; the cache is keyed on a canonical fetch URL
109
+ * with the fragment removed, because a fragment is never sent to an origin.
110
+ *
111
+ * `canHide` reflects "the viewer authored this comment" for rendering only. It
112
+ * is never an authorization decision — D7 re-checks authorship server-side on
113
+ * every hide, and a flipped flag earns a 404.
114
+ */
115
+ export interface ThreadLinkPreview {
116
+ /** SHA-256 of the canonical fetch URL, 64 lowercase hex characters. */
117
+ readonly urlHash: string
118
+ /** The normalized href for THIS occurrence, fragment included. */
119
+ readonly href: string
120
+ readonly title: string
121
+ /** Worker-derived display host. Not `og:site_name`. */
122
+ readonly domain: string
123
+ readonly description?: string | null | undefined
124
+ readonly imageUrl?: string | null | undefined
125
+ readonly siteName?: string | null | undefined
126
+ /**
127
+ * `og:image:width / og:image:height`, already clamped to `0.1..10` by the
128
+ * worker (ADR-150 D4). Absent means the card declares a 16:9 slot — the point
129
+ * being that it declares SOMETHING before the image loads, and keeps it if
130
+ * the image never does.
131
+ */
132
+ readonly imageAspect?: number | null | undefined
133
+ readonly canHide: boolean
134
+ }
135
+
136
+ /** -1 | 0 | 1. The community/roadmap variant (D8) — never combined with reactions. */
137
+ export type ThreadVote = -1 | 0 | 1
138
+
139
+ export interface ThreadComment {
140
+ readonly id: string
141
+ /** Absent or null = a root comment. */
142
+ readonly parentId?: string | null | undefined
143
+ /** Null = the author record is gone (D5). Renders degraded, never crashes. */
144
+ readonly author?: ThreadAuthor | null | undefined
145
+ /** Markdown. Rendered through MarkdownRenderer's component map; NEVER rehype-raw (D7). */
146
+ readonly bodyMd: string
147
+ readonly createdAt: number
148
+ /** Set = "· edited" (D15). No edit history in v1. */
149
+ readonly editedAt?: number | null | undefined
150
+ /** Set = tombstone (D4). The row still renders and KEEPS its subtree. */
151
+ readonly deletedAt?: number | null | undefined
152
+ /**
153
+ * TRUE = the tombstone above was made by somebody OTHER than the author
154
+ * (FU-0031, FU-0032). Meaningless without `deletedAt`, and read nowhere else.
155
+ *
156
+ * One boolean rather than a `deletedBy` author, and that is a decision, not
157
+ * a shortcut: the wire deliberately never carries who removed a comment, so
158
+ * a field that could hold a name would be a field somebody eventually fills.
159
+ * A boolean cannot leak an identity it does not have.
160
+ *
161
+ * OPTIONAL, and absent means `[deleted]` — the render every host on 0.1.0
162
+ * already gets. A host with no moderation concept never sets it and never
163
+ * sees a word change.
164
+ */
165
+ readonly moderated?: boolean | undefined
166
+ readonly reactions?: readonly ThreadReactionCount[] | undefined
167
+ readonly attachments?: readonly ThreadAttachment[] | undefined
168
+ /**
169
+ * At most three (ADR-150 D1), already ordered by the worker and already
170
+ * filtered to visible, successful rows. Absent is the normal state for a
171
+ * comment with no links — and also what a fail-soft preview read returns
172
+ * (D10), which is why an empty list can never be distinguished from an error
173
+ * and neither one is allowed to break the comment.
174
+ */
175
+ readonly linkPreviews?: readonly ThreadLinkPreview[] | undefined
176
+ /** Vote variant only (D8). */
177
+ readonly score?: number | undefined
178
+ /** Vote variant only (D8). Lifted to props — never local state (correction 3). */
179
+ readonly viewerVote?: ThreadVote | undefined
180
+ /** Pinned / "top comment". Sorts first under `top`. */
181
+ readonly pinned?: boolean | undefined
182
+ /** Defaults to 'sent' when absent. */
183
+ readonly state?: CommentDeliveryState | undefined
184
+ /** Shown beside the retry on a failed send. */
185
+ readonly errorText?: string | undefined
186
+ }
187
+
188
+ /**
189
+ * A comment placed in the reply tree.
190
+ *
191
+ * `depth` is the TRUE stored depth (D3: "depth is stored true and rendered
192
+ * capped"). The render cap is a display concern and lives in the hook.
193
+ *
194
+ * `orphanedParentId` is the D4 distinction: non-null means this reply named a
195
+ * parent that is ABSENT from the list it was built from — "not fetched", not
196
+ * "deleted". A deleted parent is a tombstone row that is still present, so its
197
+ * children stay nested under it and this stays null.
198
+ */
199
+ export interface CommentNode extends ThreadComment {
200
+ readonly replies: readonly CommentNode[]
201
+ readonly depth: number
202
+ readonly orphanedParentId: string | null
203
+ }
204
+
205
+ export type ThreadSort = 'top' | 'new'
206
+
207
+ /**
208
+ * Reactions and votes are never both on one comment (D8): reactions are
209
+ * acknowledgement, votes are ranking. The headless layer exposes both and the
210
+ * surface picks exactly one. Task threads take reactions; community and
211
+ * roadmap take votes.
212
+ */
213
+ export type ThreadEngagement = 'reactions' | 'votes' | 'none'
214
+
215
+ /**
216
+ * One row the `@` picker may offer (ADR-149 D6).
217
+ *
218
+ * It lives here, beside the other data shapes, rather than in the rich
219
+ * composer, for one structural reason: the package barrel must stay free of
220
+ * TipTap so a thread LIST never pays for ProseMirror (ADR-148 D5), and a host
221
+ * needs this type to write its `mentionItems` callback.
222
+ *
223
+ * The picker is a CONVENIENCE, never a permission boundary. The host filters a
224
+ * list it already had; the server still parses `body_md` at write time and
225
+ * re-checks every mention it extracts, exactly as it does for one typed by
226
+ * hand or pasted (ADR-147 D11, restated in ADR-149 D6).
227
+ */
228
+ export interface MentionSuggestionItem {
229
+ readonly id: string
230
+ readonly label: string
231
+ readonly type: MentionTargetType
232
+ }