@jant/core 0.5.4 → 0.6.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.
Files changed (90) hide show
  1. package/bin/commands/telegram/register-webhooks.js +93 -0
  2. package/dist/app-CMSW_AYG.js +6 -0
  3. package/dist/{app-BtNdUAqz.js → app-DYQdDMs8.js} +2249 -387
  4. package/dist/client/.vite/manifest.json +3 -3
  5. package/dist/client/_assets/client-BRTh1ii1.js +274 -0
  6. package/dist/client/_assets/client-CO4b-RKd.css +2 -0
  7. package/dist/client/_assets/{client-auth-DJ_5wx9N.js → client-auth-CSNcTJwP.js} +81 -81
  8. package/dist/{env-CgaH9Mut.js → env-C7e2Nlnt.js} +30 -1
  9. package/dist/{export-CR9Megtb.js → export-Bbn86HmS.js} +1 -1
  10. package/dist/{github-sync-DYZq9rQp.js → github-sync-CBQPRZ8H.js} +1 -1
  11. package/dist/{github-sync-8Vv06aCr.js → github-sync-dXsiZa_e.js} +2 -2
  12. package/dist/index.js +4 -4
  13. package/dist/node.js +61 -5
  14. package/package.json +2 -1
  15. package/src/__tests__/helpers/app.ts +15 -2
  16. package/src/app.tsx +3 -0
  17. package/src/client/thread-context.ts +146 -2
  18. package/src/client/tiptap/__tests__/link-toolbar.test.ts +1 -1
  19. package/src/client/tiptap/bubble-menu.ts +1 -16
  20. package/src/client/tiptap/extensions.ts +2 -6
  21. package/src/client/tiptap/link-toolbar.ts +0 -21
  22. package/src/client/tiptap/toolbar-mode.ts +0 -43
  23. package/src/db/migrations/0022_old_gressill.sql +24 -0
  24. package/src/db/migrations/0023_broad_terror.sql +20 -0
  25. package/src/db/migrations/0024_red_the_twelve.sql +3 -0
  26. package/src/db/migrations/0025_exotic_wendell_rand.sql +1 -0
  27. package/src/db/migrations/meta/0022_snapshot.json +2267 -0
  28. package/src/db/migrations/meta/0023_snapshot.json +2396 -0
  29. package/src/db/migrations/meta/0024_snapshot.json +2417 -0
  30. package/src/db/migrations/meta/0025_snapshot.json +2424 -0
  31. package/src/db/migrations/meta/_journal.json +28 -0
  32. package/src/db/migrations/pg/0020_bizarre_smasher.sql +24 -0
  33. package/src/db/migrations/pg/0021_sharp_puppet_master.sql +20 -0
  34. package/src/db/migrations/pg/0022_blushing_blue_shield.sql +3 -0
  35. package/src/db/migrations/pg/0023_organic_zemo.sql +1 -0
  36. package/src/db/migrations/pg/meta/0020_snapshot.json +2904 -0
  37. package/src/db/migrations/pg/meta/0021_snapshot.json +3060 -0
  38. package/src/db/migrations/pg/meta/0022_snapshot.json +3078 -0
  39. package/src/db/migrations/pg/meta/0023_snapshot.json +3084 -0
  40. package/src/db/migrations/pg/meta/_journal.json +28 -0
  41. package/src/db/pg/schema.ts +82 -0
  42. package/src/db/schema.ts +90 -0
  43. package/src/i18n/coverage.generated.ts +2 -2
  44. package/src/i18n/locales/public/en.po +8 -0
  45. package/src/i18n/locales/public/zh-Hans.po +8 -0
  46. package/src/i18n/locales/public/zh-Hant.po +8 -0
  47. package/src/i18n/locales/settings/en.po +135 -0
  48. package/src/i18n/locales/settings/en.ts +1 -1
  49. package/src/i18n/locales/settings/zh-Hans.po +136 -1
  50. package/src/i18n/locales/settings/zh-Hans.ts +1 -1
  51. package/src/i18n/locales/settings/zh-Hant.po +136 -1
  52. package/src/i18n/locales/settings/zh-Hant.ts +1 -1
  53. package/src/lib/__tests__/image-dimensions.test.ts +314 -0
  54. package/src/lib/__tests__/telegram-entities.test.ts +180 -0
  55. package/src/lib/__tests__/telegram-pool-webhooks.test.ts +127 -0
  56. package/src/lib/env.ts +45 -0
  57. package/src/lib/ids.ts +3 -0
  58. package/src/lib/image-dimensions.ts +258 -0
  59. package/src/lib/telegram-entities.ts +240 -0
  60. package/src/lib/telegram-pool-webhooks.ts +86 -0
  61. package/src/lib/telegram-settings-status.tsx +109 -0
  62. package/src/lib/telegram.ts +363 -0
  63. package/src/node/runtime.ts +6 -0
  64. package/src/routes/api/__tests__/telegram.test.ts +612 -0
  65. package/src/routes/api/telegram.ts +782 -0
  66. package/src/routes/api/upload-multipart.ts +34 -12
  67. package/src/routes/api/upload.ts +23 -2
  68. package/src/routes/dash/settings.tsx +131 -1
  69. package/src/routes/pages/__tests__/post-page-title.test.ts +70 -0
  70. package/src/routes/pages/page.tsx +3 -2
  71. package/src/runtime/cloudflare.ts +20 -9
  72. package/src/runtime/node.ts +20 -9
  73. package/src/runtime/site.ts +2 -1
  74. package/src/services/__tests__/telegram.test.ts +148 -0
  75. package/src/services/index.ts +9 -0
  76. package/src/services/telegram.ts +613 -0
  77. package/src/services/upload-session.ts +39 -12
  78. package/src/styles/tokens.css +1 -0
  79. package/src/styles/ui.css +117 -38
  80. package/src/types/app-context.ts +6 -0
  81. package/src/types/bindings.ts +3 -0
  82. package/src/types/config.ts +40 -0
  83. package/src/ui/dash/settings/SettingsRootContent.tsx +48 -17
  84. package/src/ui/dash/settings/TelegramContent.tsx +549 -0
  85. package/src/ui/feed/ThreadPreview.tsx +90 -38
  86. package/src/ui/feed/__tests__/thread-preview.test.ts +66 -5
  87. package/src/ui/pages/PostPage.tsx +77 -15
  88. package/dist/app-DLINgGBd.js +0 -6
  89. package/dist/client/_assets/client-BErXNT6k.css +0 -2
  90. package/dist/client/_assets/client-CtAgWT8i.js +0 -274
@@ -196,7 +196,7 @@ describe("getThreadPreviewState", () => {
196
196
  expect(html).not.toContain('id="continue"');
197
197
  });
198
198
 
199
- it("renders ancestor context fully without collapse affordances", () => {
199
+ it("wraps ancestor context in a collapsible shell with a show-more toggle when 2+ extra items precede the latest reply", () => {
200
200
  const html = renderWithI18n(() =>
201
201
  ThreadPreview({
202
202
  rootPost: createPostView({
@@ -227,10 +227,71 @@ describe("getThreadPreviewState", () => {
227
227
  }),
228
228
  );
229
229
 
230
- expect(html).not.toContain("data-thread-context");
231
- expect(html).not.toContain("data-thread-context-toggle");
232
- expect(html).not.toContain("thread-context-collapsed");
233
- expect(html).not.toContain("thread-context-fade");
230
+ expect(html).toContain("thread-context-shell");
231
+ expect(html).toContain("data-thread-context");
232
+ expect(html).toContain("data-collapsed");
233
+ expect(html).toContain("data-thread-context-toggle");
234
+ expect(html).toContain('aria-expanded="false"');
235
+ expect(html).toMatch(/data-label-more="[^"]+"/);
236
+ expect(html).toMatch(/data-label-less="[^"]+"/);
237
+ });
238
+
239
+ it("still wraps a root-only preview in the shell so the cap applies to long single posts", () => {
240
+ // root + hero — no second/penultimate/gap. The server can't know the
241
+ // root's rendered height, so it always renders the shell + toggle and
242
+ // lets client-side measurement strip them when the root actually fits.
243
+ const html = renderWithI18n(() =>
244
+ ThreadPreview({
245
+ rootPost: createPostView({ bodyHtml: "<p>Root</p>" }),
246
+ latestReply: createPostView({
247
+ id: "post-2",
248
+ permalink: "/post-2",
249
+ slug: "post-2",
250
+ bodyHtml: "<p>Latest</p>",
251
+ isLastInThread: true,
252
+ }),
253
+ totalReplyCount: 1,
254
+ }),
255
+ );
256
+
257
+ expect(html).toContain("thread-context-shell");
258
+ expect(html).toContain("data-collapsed");
259
+ expect(html).toContain("data-thread-context-toggle");
260
+ expect(html).toContain("<p>Root</p>");
261
+ expect(html).toContain("<p>Latest</p>");
262
+ });
263
+
264
+ it("renders the collapsible shell even when just one extra item precedes the latest reply", () => {
265
+ // root + secondReply + hero — one extra context item. The server can't
266
+ // measure actual height, so it renders the shell assuming overflow (the
267
+ // common case). Client-side JS then removes the cap + hides the toggle
268
+ // if the rendered content actually fits without cropping.
269
+ const html = renderWithI18n(() =>
270
+ ThreadPreview({
271
+ rootPost: createPostView({ bodyHtml: "<p>Root</p>" }),
272
+ secondReply: createPostView({
273
+ id: "post-2",
274
+ permalink: "/post-2",
275
+ slug: "post-2",
276
+ bodyHtml: "<p>Second</p>",
277
+ }),
278
+ latestReply: createPostView({
279
+ id: "post-3",
280
+ permalink: "/post-3",
281
+ slug: "post-3",
282
+ bodyHtml: "<p>Latest</p>",
283
+ isLastInThread: true,
284
+ }),
285
+ totalReplyCount: 2,
286
+ }),
287
+ );
288
+
289
+ expect(html).toContain("thread-context-shell");
290
+ expect(html).toContain("data-collapsed");
291
+ expect(html).toContain("data-thread-context-toggle");
292
+ expect(html).toContain("<p>Root</p>");
293
+ expect(html).toContain("<p>Second</p>");
294
+ expect(html).toContain("<p>Latest</p>");
234
295
  });
235
296
 
236
297
  it("points the hidden-posts gap link to the second reply so the detail page opens just above the hidden range", () => {
@@ -3,36 +3,98 @@
3
3
  *
4
4
  * Single post view — clean, no card border, with divider footer.
5
5
  * When `threadPosts` is provided, renders the full thread with the current
6
- * post highlighted and scroll-targeted.
6
+ * post highlighted and scroll-targeted. Ancestors above the current post are
7
+ * wrapped in the same collapsible shell used on the home feed
8
+ * (`.thread-context-shell`), driven by the shared `thread-context.ts`
9
+ * client logic.
7
10
  */
8
11
 
12
+ import { msg } from "@lingui/core/macro";
9
13
  import type { FC } from "hono/jsx";
14
+ import { useLingui } from "../../i18n/context.js";
10
15
  import type { PostPageProps, PostView } from "../../types.js";
11
16
  import { TimelineItemFromPost } from "../feed/TimelineItem.js";
12
17
 
18
+ const renderThreadItem = (tp: PostView, currentId: string) => {
19
+ const isCurrent = tp.id === currentId;
20
+ return (
21
+ <div
22
+ key={tp.id}
23
+ id={`post-${tp.id}`}
24
+ class={`thread-item thread-detail-item${isCurrent ? " thread-item-current" : ""}`}
25
+ {...(isCurrent ? { "data-post-current": "" } : {})}
26
+ >
27
+ <TimelineItemFromPost
28
+ post={tp}
29
+ mode="detail"
30
+ display={{ footer: { hideTimestamp: false } }}
31
+ />
32
+ </div>
33
+ );
34
+ };
35
+
13
36
  const ThreadDetail: FC<{ post: PostView; threadPosts: PostView[] }> = ({
14
37
  post,
15
38
  threadPosts,
16
39
  }) => {
40
+ const { i18n } = useLingui();
41
+ const showMoreLabel = i18n._(
42
+ msg({
43
+ message: "Show more",
44
+ comment: "@context: Expand faded thread ancestor context in the feed",
45
+ }),
46
+ );
47
+ const showLessLabel = i18n._(
48
+ msg({
49
+ message: "Show less",
50
+ comment:
51
+ "@context: Collapse expanded thread ancestor context in the feed",
52
+ }),
53
+ );
54
+
55
+ const currentIndex = threadPosts.findIndex((tp) => tp.id === post.id);
56
+ const ancestors = currentIndex > 0 ? threadPosts.slice(0, currentIndex) : [];
57
+ const currentAndAfter =
58
+ currentIndex >= 0 ? threadPosts.slice(currentIndex) : threadPosts;
59
+
17
60
  return (
18
61
  <div class="thread-group thread-group-detail" data-page="post">
19
- {threadPosts.map((tp) => {
20
- const isCurrent = tp.id === post.id;
21
- return (
62
+ {ancestors.length > 0 && (
63
+ <>
22
64
  <div
23
- key={tp.id}
24
- id={`post-${tp.id}`}
25
- class={`thread-item thread-detail-item${isCurrent ? " thread-item-current" : ""}`}
26
- {...(isCurrent ? { "data-post-current": "" } : {})}
65
+ class="thread-context-shell"
66
+ data-thread-context
67
+ data-collapsed=""
27
68
  >
28
- <TimelineItemFromPost
29
- post={tp}
30
- mode="detail"
31
- display={{ footer: { hideTimestamp: false } }}
32
- />
69
+ {ancestors.map((tp) => renderThreadItem(tp, post.id))}
33
70
  </div>
34
- );
35
- })}
71
+ <button
72
+ type="button"
73
+ class="thread-context-toggle"
74
+ data-thread-context-toggle
75
+ data-label-more={showMoreLabel}
76
+ data-label-less={showLessLabel}
77
+ aria-expanded="false"
78
+ >
79
+ <span class="thread-context-toggle-label">{showMoreLabel}</span>
80
+ <svg
81
+ class="thread-context-toggle-chevron"
82
+ viewBox="0 0 16 16"
83
+ aria-hidden="true"
84
+ >
85
+ <path
86
+ d="M4 6l4 4 4-4"
87
+ fill="none"
88
+ stroke="currentColor"
89
+ stroke-width="1.5"
90
+ stroke-linecap="round"
91
+ stroke-linejoin="round"
92
+ />
93
+ </svg>
94
+ </button>
95
+ </>
96
+ )}
97
+ {currentAndAfter.map((tp) => renderThreadItem(tp, post.id))}
36
98
  </div>
37
99
  );
38
100
  };
@@ -1,6 +0,0 @@
1
- import "./url-umUptr5z.js";
2
- import { t as createApp } from "./app-BtNdUAqz.js";
3
- import "./export-CR9Megtb.js";
4
- import "./env-CgaH9Mut.js";
5
- import "./github-sync-DYZq9rQp.js";
6
- export { createApp };