@btst/stack 2.11.7 → 2.12.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 (77) hide show
  1. package/dist/packages/stack/src/plugins/blog/client/components/pages/post-page.internal.cjs +2 -6
  2. package/dist/packages/stack/src/plugins/blog/client/components/pages/post-page.internal.mjs +5 -9
  3. package/dist/packages/stack/src/plugins/blog/client/components/shared/collapsible-tag-list.cjs +44 -0
  4. package/dist/packages/stack/src/plugins/blog/client/components/shared/collapsible-tag-list.mjs +42 -0
  5. package/dist/packages/stack/src/plugins/blog/client/components/shared/tags-list.cjs +2 -8
  6. package/dist/packages/stack/src/plugins/blog/client/components/shared/tags-list.mjs +2 -8
  7. package/dist/packages/stack/src/plugins/blog/client/localization/blog-common.cjs +3 -1
  8. package/dist/packages/stack/src/plugins/blog/client/localization/blog-common.mjs +3 -1
  9. package/dist/packages/stack/src/plugins/comments/api/query-key-defs.cjs +2 -1
  10. package/dist/packages/stack/src/plugins/comments/api/query-key-defs.mjs +2 -1
  11. package/dist/packages/stack/src/plugins/comments/client/components/comment-thread.cjs +6 -2
  12. package/dist/packages/stack/src/plugins/comments/client/components/comment-thread.mjs +6 -2
  13. package/dist/packages/stack/src/plugins/comments/client/hooks/use-comments.cjs +5 -3
  14. package/dist/packages/stack/src/plugins/comments/client/hooks/use-comments.mjs +5 -3
  15. package/dist/plugins/blog/api/index.d.cts +2 -2
  16. package/dist/plugins/blog/api/index.d.mts +2 -2
  17. package/dist/plugins/blog/api/index.d.ts +2 -2
  18. package/dist/plugins/blog/client/hooks/index.d.cts +2 -2
  19. package/dist/plugins/blog/client/hooks/index.d.mts +2 -2
  20. package/dist/plugins/blog/client/hooks/index.d.ts +2 -2
  21. package/dist/plugins/blog/client/index.d.cts +4 -2
  22. package/dist/plugins/blog/client/index.d.mts +4 -2
  23. package/dist/plugins/blog/client/index.d.ts +4 -2
  24. package/dist/plugins/blog/query-keys.d.cts +2 -2
  25. package/dist/plugins/blog/query-keys.d.mts +2 -2
  26. package/dist/plugins/blog/query-keys.d.ts +2 -2
  27. package/dist/plugins/comments/api/index.d.cts +4 -4
  28. package/dist/plugins/comments/api/index.d.mts +4 -4
  29. package/dist/plugins/comments/api/index.d.ts +4 -4
  30. package/dist/plugins/comments/client/components/index.d.cts +11 -0
  31. package/dist/plugins/comments/client/components/index.d.mts +11 -0
  32. package/dist/plugins/comments/client/components/index.d.ts +11 -0
  33. package/dist/plugins/comments/client/hooks/index.d.cts +17 -1
  34. package/dist/plugins/comments/client/hooks/index.d.mts +17 -1
  35. package/dist/plugins/comments/client/hooks/index.d.ts +17 -1
  36. package/dist/plugins/comments/client/index.d.cts +9 -0
  37. package/dist/plugins/comments/client/index.d.mts +9 -0
  38. package/dist/plugins/comments/client/index.d.ts +9 -0
  39. package/dist/plugins/comments/query-keys.cjs +1 -0
  40. package/dist/plugins/comments/query-keys.d.cts +3 -2
  41. package/dist/plugins/comments/query-keys.d.mts +3 -2
  42. package/dist/plugins/comments/query-keys.d.ts +3 -2
  43. package/dist/plugins/comments/query-keys.mjs +1 -0
  44. package/dist/plugins/kanban/api/index.d.cts +1 -1
  45. package/dist/plugins/kanban/api/index.d.mts +1 -1
  46. package/dist/plugins/kanban/api/index.d.ts +1 -1
  47. package/dist/plugins/kanban/query-keys.d.cts +1 -1
  48. package/dist/plugins/kanban/query-keys.d.mts +1 -1
  49. package/dist/plugins/kanban/query-keys.d.ts +1 -1
  50. package/dist/shared/{stack.CLWhMntf.d.cts → stack.04aMRz_W.d.cts} +5 -5
  51. package/dist/shared/{stack.CJE9sAjV.d.ts → stack.Bq3kiUaM.d.ts} +1 -1
  52. package/dist/shared/{stack.DNLuzDb3.d.cts → stack.BvuqACtA.d.cts} +3 -3
  53. package/dist/shared/{stack.C-b3Sn8j.d.cts → stack.C7KNNiJm.d.cts} +3 -1
  54. package/dist/shared/{stack.C-b3Sn8j.d.mts → stack.C7KNNiJm.d.mts} +3 -1
  55. package/dist/shared/{stack.C-b3Sn8j.d.ts → stack.C7KNNiJm.d.ts} +3 -1
  56. package/dist/shared/{stack.Dcz6636A.d.mts → stack.CCncwK6g.d.cts} +1 -1
  57. package/dist/shared/{stack.CmHRdhl8.d.cts → stack.CO1g47IS.d.mts} +1 -1
  58. package/dist/shared/{stack.Dfc44JiE.d.mts → stack.Cs8R5HxD.d.mts} +3 -3
  59. package/dist/shared/{stack.ufcExQIR.d.ts → stack.CxWEN62M.d.ts} +5 -5
  60. package/dist/shared/{stack.BnSAhKJi.d.ts → stack.DPTRuLUH.d.ts} +3 -3
  61. package/dist/shared/{stack.DiodAFAa.d.mts → stack.ueI-K4xy.d.mts} +5 -5
  62. package/package.json +1 -1
  63. package/src/plugins/blog/client/components/pages/post-page.internal.tsx +4 -21
  64. package/src/plugins/blog/client/components/shared/collapsible-tag-list.tsx +76 -0
  65. package/src/plugins/blog/client/components/shared/tags-list.tsx +2 -18
  66. package/src/plugins/blog/client/localization/blog-common.ts +2 -0
  67. package/src/plugins/comments/api/query-key-defs.ts +5 -1
  68. package/src/plugins/comments/client/components/comment-thread.tsx +15 -0
  69. package/src/plugins/comments/client/hooks/use-comments.tsx +26 -3
  70. package/src/plugins/comments/client/overrides.ts +10 -0
  71. package/src/plugins/comments/query-keys.ts +2 -0
  72. package/dist/shared/{stack.Rtcvl8sS.d.cts → stack.B6S3cgwN.d.cts} +13 -13
  73. package/dist/shared/{stack.HE_IvqV5.d.mts → stack.Bzfx-_lq.d.mts} +13 -13
  74. package/dist/shared/{stack.CuhcFsAx.d.cts → stack.DuQOcInS.d.cts} +3 -3
  75. package/dist/shared/{stack.CuhcFsAx.d.mts → stack.DuQOcInS.d.mts} +3 -3
  76. package/dist/shared/{stack.CuhcFsAx.d.ts → stack.DuQOcInS.d.ts} +3 -3
  77. package/dist/shared/{stack.D4Cea8II.d.ts → stack.j5SFLC1d.d.ts} +13 -13
@@ -6,7 +6,7 @@ import * as _btst_stack_plugins_api from '@btst/stack/plugins/api';
6
6
  import { DBAdapter } from '@btst/db';
7
7
  import * as better_call from 'better-call';
8
8
  import { z } from 'zod';
9
- import { c as createPostSchema, u as updatePostSchema } from './stack.CuhcFsAx.mjs';
9
+ import { c as createPostSchema, u as updatePostSchema } from './stack.DuQOcInS.mjs';
10
10
 
11
11
  /**
12
12
  * Parameters for filtering/paginating posts.
@@ -325,11 +325,11 @@ declare const blogBackendPlugin: (hooks?: BlogBackendHooks) => _btst_stack_plugi
325
325
  slug: string;
326
326
  })[] | undefined;
327
327
  slug?: string | undefined;
328
- published?: boolean | undefined;
329
- publishedAt?: unknown;
330
328
  createdAt?: unknown;
331
329
  updatedAt?: unknown;
330
+ publishedAt?: unknown;
332
331
  image?: string | undefined;
332
+ published?: boolean | undefined;
333
333
  }, {
334
334
  title: string;
335
335
  content: string;
@@ -342,11 +342,11 @@ declare const blogBackendPlugin: (hooks?: BlogBackendHooks) => _btst_stack_plugi
342
342
  slug: string;
343
343
  })[] | undefined;
344
344
  slug?: string | undefined;
345
- published?: boolean | undefined;
346
- publishedAt?: unknown;
347
345
  createdAt?: unknown;
348
346
  updatedAt?: unknown;
347
+ publishedAt?: unknown;
349
348
  image?: string | undefined;
349
+ published?: boolean | undefined;
350
350
  }>;
351
351
  }, Post>;
352
352
  readonly updatePost: better_call.StrictEndpoint<"/posts/:id", {} & {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@btst/stack",
3
- "version": "2.11.7",
3
+ "version": "2.12.0",
4
4
  "description": "A composable, plugin-based library for building full-stack applications.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
 
3
- import { usePluginOverrides, useBasePath } from "@btst/stack/context";
3
+ import { usePluginOverrides } from "@btst/stack/context";
4
4
  import { formatDate } from "date-fns";
5
5
  import {
6
6
  useSuspensePost,
@@ -12,11 +12,10 @@ import { MarkdownContent } from "../shared/markdown-content";
12
12
  import { PageHeader } from "../shared/page-header";
13
13
  import { PageWrapper } from "../shared/page-wrapper";
14
14
  import type { BlogPluginOverrides } from "../../overrides";
15
- import { DefaultImage, DefaultLink } from "../shared/defaults";
15
+ import { DefaultImage } from "../shared/defaults";
16
16
  import { BLOG_LOCALIZATION } from "../../localization";
17
17
  import { PostNavigation } from "../shared/post-navigation";
18
18
  import { RecentPostsCarousel } from "../shared/recent-posts-carousel";
19
- import { Badge } from "@workspace/ui/components/badge";
20
19
  import { useRouteLifecycle } from "@workspace/ui/hooks/use-route-lifecycle";
21
20
  import { OnThisPage, OnThisPageSelect } from "../shared/on-this-page";
22
21
  import type { SerializedPost } from "../../../types";
@@ -24,6 +23,7 @@ import { useRegisterPageAIContext } from "@btst/stack/plugins/ai-chat/client/con
24
23
  import { WhenVisible } from "@workspace/ui/components/when-visible";
25
24
  import { PostNavigationSkeleton } from "../loading/post-navigation-skeleton";
26
25
  import { RecentPostsCarouselSkeleton } from "../loading/recent-posts-carousel-skeleton";
26
+ import { CollapsibleTagList } from "../shared/collapsible-tag-list";
27
27
 
28
28
  // Internal component with actual page content
29
29
  export function PostPage({ slug }: { slug: string }) {
@@ -151,29 +151,12 @@ export function PostPage({ slug }: { slug: string }) {
151
151
  }
152
152
 
153
153
  function PostHeaderTop({ post }: { post: SerializedPost }) {
154
- const { Link } = usePluginOverrides<
155
- BlogPluginOverrides,
156
- Partial<BlogPluginOverrides>
157
- >("blog", {
158
- Link: DefaultLink,
159
- });
160
- const basePath = useBasePath();
161
154
  return (
162
155
  <div className="flex flex-row items-center justify-center gap-2 flex-wrap mt-8">
163
156
  <span className="font-light text-muted-foreground text-sm">
164
157
  {formatDate(post.createdAt, "MMMM d, yyyy")}
165
158
  </span>
166
- {post.tags && post.tags.length > 0 && (
167
- <>
168
- {post.tags.map((tag) => (
169
- <Link key={tag.id} href={`${basePath}/blog/tag/${tag.slug}`}>
170
- <Badge variant="secondary" className="text-xs">
171
- {tag.name}
172
- </Badge>
173
- </Link>
174
- ))}
175
- </>
176
- )}
159
+ <CollapsibleTagList tags={post.tags ?? []} />
177
160
  </div>
178
161
  );
179
162
  }
@@ -0,0 +1,76 @@
1
+ "use client";
2
+
3
+ import { usePluginOverrides, useBasePath } from "@btst/stack/context";
4
+ import type { BlogPluginOverrides } from "../../overrides";
5
+ import { DefaultLink } from "./defaults";
6
+ import { Badge } from "@workspace/ui/components/badge";
7
+ import { BLOG_LOCALIZATION } from "../../localization";
8
+ import { ChevronDown, ChevronUp } from "lucide-react";
9
+ import { useState } from "react";
10
+ import type { SerializedTag } from "../../../types";
11
+
12
+ const MAX_VISIBLE_TAGS = 15;
13
+
14
+ interface CollapsibleTagListProps {
15
+ tags: SerializedTag[];
16
+ maxVisible?: number;
17
+ }
18
+
19
+ export function CollapsibleTagList({
20
+ tags,
21
+ maxVisible = MAX_VISIBLE_TAGS,
22
+ }: CollapsibleTagListProps) {
23
+ const { Link, localization } = usePluginOverrides<
24
+ BlogPluginOverrides,
25
+ Partial<BlogPluginOverrides>
26
+ >("blog", {
27
+ Link: DefaultLink,
28
+ localization: BLOG_LOCALIZATION,
29
+ });
30
+ const basePath = useBasePath();
31
+ const [showAll, setShowAll] = useState(false);
32
+
33
+ if (!tags || tags.length === 0) {
34
+ return null;
35
+ }
36
+
37
+ const hasMore = tags.length > maxVisible;
38
+ const visibleTags = showAll || !hasMore ? tags : tags.slice(0, maxVisible);
39
+
40
+ return (
41
+ <>
42
+ {visibleTags.map((tag) => (
43
+ <Link key={tag.id} href={`${basePath}/blog/tag/${tag.slug}`}>
44
+ <Badge variant="secondary" className="text-xs">
45
+ {tag.name}
46
+ </Badge>
47
+ </Link>
48
+ ))}
49
+ {hasMore && (
50
+ <Badge asChild variant="secondary" className="text-xs cursor-pointer">
51
+ <button
52
+ type="button"
53
+ onClick={() => setShowAll((prev) => !prev)}
54
+ aria-expanded={showAll}
55
+ aria-label={
56
+ showAll
57
+ ? localization.BLOG_TAGS_SHOW_LESS
58
+ : localization.BLOG_TAGS_SHOW_ALL
59
+ }
60
+ title={
61
+ showAll
62
+ ? localization.BLOG_TAGS_SHOW_LESS
63
+ : localization.BLOG_TAGS_SHOW_ALL
64
+ }
65
+ >
66
+ {showAll ? (
67
+ <ChevronUp aria-hidden="true" />
68
+ ) : (
69
+ <ChevronDown aria-hidden="true" />
70
+ )}
71
+ </button>
72
+ </Badge>
73
+ )}
74
+ </>
75
+ );
76
+ }
@@ -1,20 +1,10 @@
1
1
  "use client";
2
2
 
3
- import { usePluginOverrides, useBasePath } from "@btst/stack/context";
4
- import type { BlogPluginOverrides } from "../../overrides";
5
- import { DefaultLink } from "./defaults";
6
- import { Badge } from "@workspace/ui/components/badge";
7
3
  import { useSuspenseTags } from "../../hooks/blog-hooks";
4
+ import { CollapsibleTagList } from "./collapsible-tag-list";
8
5
 
9
6
  export function TagsList() {
10
7
  const { tags } = useSuspenseTags();
11
- const { Link } = usePluginOverrides<
12
- BlogPluginOverrides,
13
- Partial<BlogPluginOverrides>
14
- >("blog", {
15
- Link: DefaultLink,
16
- });
17
- const basePath = useBasePath();
18
8
 
19
9
  if (!tags || tags.length === 0) {
20
10
  return null;
@@ -22,13 +12,7 @@ export function TagsList() {
22
12
 
23
13
  return (
24
14
  <div className="flex flex-wrap gap-2 justify-center">
25
- {tags.map((tag) => (
26
- <Link key={tag.id} href={`${basePath}/blog/tag/${tag.slug}`}>
27
- <Badge variant="secondary" className="text-xs">
28
- {tag.name}
29
- </Badge>
30
- </Link>
31
- ))}
15
+ <CollapsibleTagList tags={tags} />
32
16
  </div>
33
17
  );
34
18
  }
@@ -4,4 +4,6 @@ export const BLOG_COMMON = {
4
4
  BLOG_PAGE_NOT_FOUND_TITLE: "Not Found",
5
5
  BLOG_PAGE_NOT_FOUND_DESCRIPTION:
6
6
  "The page you are looking for does not exist.",
7
+ BLOG_TAGS_SHOW_ALL: "Show all tags",
8
+ BLOG_TAGS_SHOW_LESS: "Show fewer tags",
7
9
  };
@@ -71,6 +71,7 @@ export interface CommentsThreadDiscriminator {
71
71
  parentId: string | null | undefined;
72
72
  status: string | undefined;
73
73
  currentUserId: string | undefined;
74
+ sort: string | undefined;
74
75
  limit: number;
75
76
  }
76
77
 
@@ -80,6 +81,7 @@ export function commentsThreadDiscriminator(params?: {
80
81
  parentId?: string | null;
81
82
  status?: string;
82
83
  currentUserId?: string;
84
+ sort?: string;
83
85
  limit?: number;
84
86
  }): CommentsThreadDiscriminator {
85
87
  return {
@@ -88,6 +90,7 @@ export function commentsThreadDiscriminator(params?: {
88
90
  parentId: params?.parentId,
89
91
  status: params?.status,
90
92
  currentUserId: params?.currentUserId,
93
+ sort: params?.sort,
91
94
  limit: params?.limit ?? 20,
92
95
  };
93
96
  }
@@ -128,7 +131,7 @@ export const COMMENTS_QUERY_KEYS = {
128
131
 
129
132
  /**
130
133
  * Key for the infinite thread query (top-level comments, load-more).
131
- * Full key: ["commentsThread", "list", { resourceId, resourceType, parentId, status, currentUserId, limit }]
134
+ * Full key: ["commentsThread", "list", { resourceId, resourceType, parentId, status, currentUserId, sort, limit }]
132
135
  * Offset is excluded — it is driven by `pageParam`, not baked into the key.
133
136
  */
134
137
  commentsThread: (params?: {
@@ -137,6 +140,7 @@ export const COMMENTS_QUERY_KEYS = {
137
140
  parentId?: string | null;
138
141
  status?: string;
139
142
  currentUserId?: string;
143
+ sort?: string;
140
144
  limit?: number;
141
145
  }) =>
142
146
  ["commentsThread", "list", commentsThreadDiscriminator(params)] as const,
@@ -98,6 +98,17 @@ export interface CommentThreadProps {
98
98
  * Defaults to true.
99
99
  */
100
100
  allowEditing?: boolean;
101
+ /**
102
+ * Sort direction for top-level comments by `createdAt`.
103
+ * - `"desc"` (default): newest first.
104
+ * - `"asc"`: oldest first.
105
+ *
106
+ * Replies inside each thread always render chronologically (oldest → newest)
107
+ * and are unaffected by this prop.
108
+ *
109
+ * Overrides the global `defaultCommentSort` from `CommentsPluginOverrides`.
110
+ */
111
+ sort?: "asc" | "desc";
101
112
  }
102
113
 
103
114
  const DEFAULT_RENDERER: ComponentType<CommentRendererProps> = ({ body }) => (
@@ -325,6 +336,7 @@ function CommentThreadInner({
325
336
  pageSize: pageSizeProp,
326
337
  allowPosting: allowPostingProp,
327
338
  allowEditing: allowEditingProp,
339
+ sort: sortProp,
328
340
  }: CommentThreadProps) {
329
341
  const overrides = usePluginOverrides<
330
342
  CommentsPluginOverrides,
@@ -334,6 +346,7 @@ function CommentThreadInner({
334
346
  pageSizeProp ?? overrides.defaultCommentPageSize ?? DEFAULT_PAGE_SIZE;
335
347
  const allowPosting = allowPostingProp ?? overrides.allowPosting ?? true;
336
348
  const allowEditing = allowEditingProp ?? overrides.allowEditing ?? true;
349
+ const sort = sortProp ?? overrides.defaultCommentSort ?? "desc";
337
350
  const loc = { ...COMMENTS_LOCALIZATION, ...localizationProp };
338
351
  const [replyingTo, setReplyingTo] = useState<string | null>(null);
339
352
  const [expandedReplies, setExpandedReplies] = useState<Set<string>>(
@@ -357,6 +370,7 @@ function CommentThreadInner({
357
370
  status: "approved",
358
371
  parentId: null,
359
372
  currentUserId,
373
+ sort,
360
374
  pageSize,
361
375
  });
362
376
 
@@ -366,6 +380,7 @@ function CommentThreadInner({
366
380
  currentUserId,
367
381
  infiniteKey: threadQueryKey,
368
382
  pageSize,
383
+ sort,
369
384
  });
370
385
 
371
386
  const handlePost = async (body: string) => {
@@ -164,6 +164,11 @@ export function useInfiniteComments(
164
164
  parentId?: string | null;
165
165
  status?: "pending" | "approved" | "spam";
166
166
  currentUserId?: string;
167
+ /**
168
+ * Sort direction by `createdAt`. Default: `"asc"` (oldest first) — matches
169
+ * the server-side default. Pass `"desc"` for newest-first threads.
170
+ */
171
+ sort?: "asc" | "desc";
167
172
  pageSize?: number;
168
173
  },
169
174
  options?: { enabled?: boolean },
@@ -178,6 +183,7 @@ export function useInfiniteComments(
178
183
  parentId: params.parentId ?? null,
179
184
  status: params.status,
180
185
  currentUserId: params.currentUserId,
186
+ sort: params.sort,
181
187
  limit: pageSize,
182
188
  });
183
189
 
@@ -266,6 +272,17 @@ export function usePostComment(
266
272
  * `nextOffset` from `lastPage.limit` instead of a hardcoded fallback.
267
273
  */
268
274
  pageSize?: number;
275
+ /**
276
+ * Sort direction of the surrounding infinite thread.
277
+ * - `"asc"` (default): newest comments belong on the LAST page → optimistic
278
+ * item is appended to `pages[last].items`.
279
+ * - `"desc"`: newest comments belong on the FIRST page → optimistic item
280
+ * is prepended to `pages[0].items`.
281
+ *
282
+ * Must match the `sort` passed to `useInfiniteComments` so the optimistic
283
+ * item appears in the same position the server will place it.
284
+ */
285
+ sort?: "asc" | "desc";
269
286
  },
270
287
  ) {
271
288
  const queryClient = useQueryClient();
@@ -350,6 +367,7 @@ export function usePostComment(
350
367
  const previous =
351
368
  queryClient.getQueryData<InfiniteData<CommentListResult>>(listKey);
352
369
 
370
+ const isDesc = params.sort === "desc";
353
371
  queryClient.setQueryData<InfiniteData<CommentListResult>>(
354
372
  listKey,
355
373
  (old) => {
@@ -366,16 +384,21 @@ export function usePostComment(
366
384
  pageParams: [0],
367
385
  };
368
386
  }
369
- const lastIdx = old.pages.length - 1;
387
+ // For asc (oldest-first) threads the new comment lives at the end →
388
+ // append to the last page. For desc (newest-first) threads it lives
389
+ // at the top → prepend to the first page.
390
+ const targetIdx = isDesc ? 0 : old.pages.length - 1;
370
391
  return {
371
392
  ...old,
372
393
  // Increment `total` on every page so the header count (which reads
373
394
  // pages[0].total) stays in sync even after multiple pages are loaded.
374
395
  pages: old.pages.map((page, idx) =>
375
- idx === lastIdx
396
+ idx === targetIdx
376
397
  ? {
377
398
  ...page,
378
- items: [...page.items, optimistic],
399
+ items: isDesc
400
+ ? [optimistic, ...page.items]
401
+ : [...page.items, optimistic],
379
402
  total: page.total + 1,
380
403
  }
381
404
  : { ...page, total: page.total + 1 },
@@ -75,6 +75,16 @@ export interface CommentsPluginOverrides {
75
75
  */
76
76
  defaultCommentPageSize?: number;
77
77
 
78
+ /**
79
+ * Default sort direction (by `createdAt`) for top-level comments in
80
+ * `CommentThread`.
81
+ * - `"desc"` (default): newest comments first.
82
+ * - `"asc"`: oldest comments first.
83
+ *
84
+ * Can be overridden per-instance via the `sort` prop on `CommentThread`.
85
+ */
86
+ defaultCommentSort?: "asc" | "desc";
87
+
78
88
  /**
79
89
  * When false, the comment form and reply buttons are hidden in all
80
90
  * `CommentThread` instances. Users can still read existing comments.
@@ -142,6 +142,7 @@ function createCommentsThreadQueries(
142
142
  parentId?: string | null;
143
143
  status?: "pending" | "approved" | "spam";
144
144
  currentUserId?: string;
145
+ sort?: "asc" | "desc";
145
146
  limit?: number;
146
147
  }) => ({
147
148
  // Offset is excluded from the key — it is driven by pageParam.
@@ -162,6 +163,7 @@ function createCommentsThreadQueries(
162
163
  // The server resolves the caller's identity server-side via the
163
164
  // resolveCurrentUserId hook. It is still included in the queryKey
164
165
  // above for client-side cache segregation.
166
+ sort: params?.sort,
165
167
  limit: params?.limit ?? 20,
166
168
  offset: pageParam ?? 0,
167
169
  },
@@ -7,12 +7,12 @@ import { QueryClient } from '@tanstack/react-query';
7
7
 
8
8
  declare const createBoardSchema: z.ZodObject<{
9
9
  description: z.ZodOptional<z.ZodString>;
10
- name: z.ZodString;
11
- createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
12
- updatedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
13
10
  slug: z.ZodOptional<z.ZodString>;
14
11
  ownerId: z.ZodOptional<z.ZodString>;
15
12
  organizationId: z.ZodOptional<z.ZodString>;
13
+ createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
14
+ updatedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
15
+ name: z.ZodString;
16
16
  }, z.core.$strip>;
17
17
  declare const updateBoardSchema: z.ZodObject<{
18
18
  createdAt: z.ZodOptional<z.ZodOptional<z.ZodCoercedDate<unknown>>>;
@@ -331,19 +331,19 @@ declare const kanbanBackendPlugin: (hooks?: KanbanBackendHooks) => _btst_stack_p
331
331
  body: better_call.StandardSchemaV1<{
332
332
  name: string;
333
333
  description?: string | undefined;
334
- createdAt?: unknown;
335
- updatedAt?: unknown;
336
334
  slug?: string | undefined;
337
335
  ownerId?: string | undefined;
338
336
  organizationId?: string | undefined;
337
+ createdAt?: unknown;
338
+ updatedAt?: unknown;
339
339
  }, {
340
340
  name: string;
341
341
  description?: string | undefined;
342
- createdAt?: unknown;
343
- updatedAt?: unknown;
344
342
  slug?: string | undefined;
345
343
  ownerId?: string | undefined;
346
344
  organizationId?: string | undefined;
345
+ createdAt?: unknown;
346
+ updatedAt?: unknown;
347
347
  }>;
348
348
  }, {
349
349
  columns: ColumnWithTasks[];
@@ -360,20 +360,20 @@ declare const kanbanBackendPlugin: (hooks?: KanbanBackendHooks) => _btst_stack_p
360
360
  method: "PUT";
361
361
  body: better_call.StandardSchemaV1<{
362
362
  description?: string | undefined;
363
- name?: string | undefined;
364
- createdAt?: unknown;
365
- updatedAt?: unknown;
366
363
  slug?: string | undefined;
367
364
  ownerId?: string | undefined;
368
365
  organizationId?: string | undefined;
369
- }, {
370
- description?: string | undefined;
371
- name?: string | undefined;
372
366
  createdAt?: unknown;
373
367
  updatedAt?: unknown;
368
+ name?: string | undefined;
369
+ }, {
370
+ description?: string | undefined;
374
371
  slug?: string | undefined;
375
372
  ownerId?: string | undefined;
376
373
  organizationId?: string | undefined;
374
+ createdAt?: unknown;
375
+ updatedAt?: unknown;
376
+ name?: string | undefined;
377
377
  }>;
378
378
  }, Board>;
379
379
  readonly deleteBoard: better_call.StrictEndpoint<"/boards/:id", {} & {
@@ -7,12 +7,12 @@ import { QueryClient } from '@tanstack/react-query';
7
7
 
8
8
  declare const createBoardSchema: z.ZodObject<{
9
9
  description: z.ZodOptional<z.ZodString>;
10
- name: z.ZodString;
11
- createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
12
- updatedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
13
10
  slug: z.ZodOptional<z.ZodString>;
14
11
  ownerId: z.ZodOptional<z.ZodString>;
15
12
  organizationId: z.ZodOptional<z.ZodString>;
13
+ createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
14
+ updatedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
15
+ name: z.ZodString;
16
16
  }, z.core.$strip>;
17
17
  declare const updateBoardSchema: z.ZodObject<{
18
18
  createdAt: z.ZodOptional<z.ZodOptional<z.ZodCoercedDate<unknown>>>;
@@ -331,19 +331,19 @@ declare const kanbanBackendPlugin: (hooks?: KanbanBackendHooks) => _btst_stack_p
331
331
  body: better_call.StandardSchemaV1<{
332
332
  name: string;
333
333
  description?: string | undefined;
334
- createdAt?: unknown;
335
- updatedAt?: unknown;
336
334
  slug?: string | undefined;
337
335
  ownerId?: string | undefined;
338
336
  organizationId?: string | undefined;
337
+ createdAt?: unknown;
338
+ updatedAt?: unknown;
339
339
  }, {
340
340
  name: string;
341
341
  description?: string | undefined;
342
- createdAt?: unknown;
343
- updatedAt?: unknown;
344
342
  slug?: string | undefined;
345
343
  ownerId?: string | undefined;
346
344
  organizationId?: string | undefined;
345
+ createdAt?: unknown;
346
+ updatedAt?: unknown;
347
347
  }>;
348
348
  }, {
349
349
  columns: ColumnWithTasks[];
@@ -360,20 +360,20 @@ declare const kanbanBackendPlugin: (hooks?: KanbanBackendHooks) => _btst_stack_p
360
360
  method: "PUT";
361
361
  body: better_call.StandardSchemaV1<{
362
362
  description?: string | undefined;
363
- name?: string | undefined;
364
- createdAt?: unknown;
365
- updatedAt?: unknown;
366
363
  slug?: string | undefined;
367
364
  ownerId?: string | undefined;
368
365
  organizationId?: string | undefined;
369
- }, {
370
- description?: string | undefined;
371
- name?: string | undefined;
372
366
  createdAt?: unknown;
373
367
  updatedAt?: unknown;
368
+ name?: string | undefined;
369
+ }, {
370
+ description?: string | undefined;
374
371
  slug?: string | undefined;
375
372
  ownerId?: string | undefined;
376
373
  organizationId?: string | undefined;
374
+ createdAt?: unknown;
375
+ updatedAt?: unknown;
376
+ name?: string | undefined;
377
377
  }>;
378
378
  }, Board>;
379
379
  readonly deleteBoard: better_call.StrictEndpoint<"/boards/:id", {} & {
@@ -8,15 +8,15 @@ declare const createPostSchema: z.ZodObject<{
8
8
  name: z.ZodString;
9
9
  slug: z.ZodString;
10
10
  }, z.core.$strip>]>>>>;
11
+ title: z.ZodString;
11
12
  slug: z.ZodOptional<z.ZodString>;
12
- published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
13
- publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
14
13
  createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
15
14
  updatedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
16
- title: z.ZodString;
15
+ publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
17
16
  content: z.ZodString;
18
17
  excerpt: z.ZodString;
19
18
  image: z.ZodOptional<z.ZodString>;
19
+ published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
20
20
  }, z.core.$strip>;
21
21
  declare const updatePostSchema: z.ZodObject<{
22
22
  publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
@@ -8,15 +8,15 @@ declare const createPostSchema: z.ZodObject<{
8
8
  name: z.ZodString;
9
9
  slug: z.ZodString;
10
10
  }, z.core.$strip>]>>>>;
11
+ title: z.ZodString;
11
12
  slug: z.ZodOptional<z.ZodString>;
12
- published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
13
- publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
14
13
  createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
15
14
  updatedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
16
- title: z.ZodString;
15
+ publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
17
16
  content: z.ZodString;
18
17
  excerpt: z.ZodString;
19
18
  image: z.ZodOptional<z.ZodString>;
19
+ published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
20
20
  }, z.core.$strip>;
21
21
  declare const updatePostSchema: z.ZodObject<{
22
22
  publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
@@ -8,15 +8,15 @@ declare const createPostSchema: z.ZodObject<{
8
8
  name: z.ZodString;
9
9
  slug: z.ZodString;
10
10
  }, z.core.$strip>]>>>>;
11
+ title: z.ZodString;
11
12
  slug: z.ZodOptional<z.ZodString>;
12
- published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
13
- publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
14
13
  createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
15
14
  updatedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
16
- title: z.ZodString;
15
+ publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
17
16
  content: z.ZodString;
18
17
  excerpt: z.ZodString;
19
18
  image: z.ZodOptional<z.ZodString>;
19
+ published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
20
20
  }, z.core.$strip>;
21
21
  declare const updatePostSchema: z.ZodObject<{
22
22
  publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;