@jant/core 0.5.3 → 0.6.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.
- package/bin/commands/telegram/register-webhooks.js +93 -0
- package/dist/{app-C481ssbr.js → app-BIkkbVQk.js} +2252 -383
- package/dist/app-Bcr5_wZI.js +6 -0
- package/dist/client/.vite/manifest.json +3 -3
- package/dist/client/_assets/client-Bo7sKkAQ.js +274 -0
- package/dist/client/_assets/client-QHRvzZwk.css +2 -0
- package/dist/client/_assets/{client-auth-CfBiCAB7.js → client-auth-D1jDQgbH.js} +49 -49
- package/dist/{env-CgaH9Mut.js → env-C7e2Nlnt.js} +30 -1
- package/dist/{export-CR9Megtb.js → export-Bbn86HmS.js} +1 -1
- package/dist/{github-sync-DYZq9rQp.js → github-sync-CBQPRZ8H.js} +1 -1
- package/dist/{github-sync-8Vv06aCr.js → github-sync-dXsiZa_e.js} +2 -2
- package/dist/index.js +4 -4
- package/dist/node.js +61 -5
- package/package.json +2 -1
- package/src/__tests__/helpers/app.ts +15 -2
- package/src/app.tsx +3 -0
- package/src/client/components/jant-compose-editor.ts +72 -0
- package/src/client/thread-context.ts +146 -2
- package/src/client/tiptap/__tests__/link-toolbar.test.ts +1 -1
- package/src/client/tiptap/bubble-menu.ts +1 -16
- package/src/client/tiptap/extensions.ts +2 -6
- package/src/client/tiptap/link-toolbar.ts +0 -21
- package/src/client/tiptap/paste-media.ts +49 -33
- package/src/client/tiptap/toolbar-mode.ts +0 -43
- package/src/client/video-processor.ts +9 -0
- package/src/db/migrations/0022_old_gressill.sql +24 -0
- package/src/db/migrations/0023_broad_terror.sql +20 -0
- package/src/db/migrations/0024_red_the_twelve.sql +3 -0
- package/src/db/migrations/0025_exotic_wendell_rand.sql +1 -0
- package/src/db/migrations/meta/0022_snapshot.json +2267 -0
- package/src/db/migrations/meta/0023_snapshot.json +2396 -0
- package/src/db/migrations/meta/0024_snapshot.json +2417 -0
- package/src/db/migrations/meta/0025_snapshot.json +2424 -0
- package/src/db/migrations/meta/_journal.json +28 -0
- package/src/db/migrations/pg/0020_bizarre_smasher.sql +24 -0
- package/src/db/migrations/pg/0021_sharp_puppet_master.sql +20 -0
- package/src/db/migrations/pg/0022_blushing_blue_shield.sql +3 -0
- package/src/db/migrations/pg/0023_organic_zemo.sql +1 -0
- package/src/db/migrations/pg/meta/0020_snapshot.json +2904 -0
- package/src/db/migrations/pg/meta/0021_snapshot.json +3060 -0
- package/src/db/migrations/pg/meta/0022_snapshot.json +3078 -0
- package/src/db/migrations/pg/meta/0023_snapshot.json +3084 -0
- package/src/db/migrations/pg/meta/_journal.json +28 -0
- package/src/db/pg/schema.ts +82 -0
- package/src/db/schema.ts +90 -0
- package/src/i18n/coverage.generated.ts +2 -2
- package/src/i18n/locales/public/en.po +8 -0
- package/src/i18n/locales/public/zh-Hans.po +8 -0
- package/src/i18n/locales/public/zh-Hant.po +8 -0
- package/src/i18n/locales/settings/en.po +135 -0
- package/src/i18n/locales/settings/en.ts +1 -1
- package/src/i18n/locales/settings/zh-Hans.po +136 -1
- package/src/i18n/locales/settings/zh-Hans.ts +1 -1
- package/src/i18n/locales/settings/zh-Hant.po +136 -1
- package/src/i18n/locales/settings/zh-Hant.ts +1 -1
- package/src/lib/__tests__/image-dimensions.test.ts +314 -0
- package/src/lib/__tests__/mp4-track-flags.test.ts +117 -0
- package/src/lib/__tests__/telegram-entities.test.ts +180 -0
- package/src/lib/__tests__/telegram-pool-webhooks.test.ts +127 -0
- package/src/lib/env.ts +45 -0
- package/src/lib/ids.ts +3 -0
- package/src/lib/image-dimensions.ts +258 -0
- package/src/lib/mp4-track-flags.ts +71 -0
- package/src/lib/telegram-entities.ts +240 -0
- package/src/lib/telegram-pool-webhooks.ts +86 -0
- package/src/lib/telegram-settings-status.tsx +109 -0
- package/src/lib/telegram.ts +363 -0
- package/src/node/runtime.ts +6 -0
- package/src/routes/api/__tests__/telegram.test.ts +612 -0
- package/src/routes/api/telegram.ts +782 -0
- package/src/routes/api/upload-multipart.ts +34 -12
- package/src/routes/api/upload.ts +23 -2
- package/src/routes/dash/settings.tsx +131 -1
- package/src/routes/pages/__tests__/post-page-title.test.ts +70 -0
- package/src/routes/pages/page.tsx +3 -2
- package/src/runtime/cloudflare.ts +20 -9
- package/src/runtime/node.ts +20 -9
- package/src/runtime/site.ts +2 -1
- package/src/services/__tests__/telegram.test.ts +148 -0
- package/src/services/index.ts +9 -0
- package/src/services/telegram.ts +613 -0
- package/src/services/upload-session.ts +39 -12
- package/src/styles/tokens.css +1 -0
- package/src/styles/ui.css +134 -38
- package/src/types/app-context.ts +6 -0
- package/src/types/bindings.ts +3 -0
- package/src/types/config.ts +40 -0
- package/src/ui/dash/settings/SettingsRootContent.tsx +48 -17
- package/src/ui/dash/settings/TelegramContent.tsx +549 -0
- package/src/ui/feed/ThreadPreview.tsx +91 -38
- package/src/ui/feed/__tests__/thread-preview.test.ts +67 -5
- package/src/ui/pages/PostPage.tsx +78 -15
- package/dist/app-BgMwEN-M.js +0 -6
- package/dist/client/_assets/client-CJQYvkEx.js +0 -274
- package/dist/client/_assets/client-CQvi1Buw.css +0 -2
|
@@ -85,9 +85,7 @@ export function createSettingsEditorExtensions(
|
|
|
85
85
|
BubbleMenu.configure({
|
|
86
86
|
toolbarMode: "compose",
|
|
87
87
|
}),
|
|
88
|
-
LinkToolbar
|
|
89
|
-
toolbarMode: "compose",
|
|
90
|
-
}),
|
|
88
|
+
LinkToolbar,
|
|
91
89
|
];
|
|
92
90
|
}
|
|
93
91
|
|
|
@@ -118,9 +116,7 @@ export function createEditorExtensions(
|
|
|
118
116
|
BubbleMenu.configure({
|
|
119
117
|
toolbarMode: options.toolbarMode ?? "default",
|
|
120
118
|
}),
|
|
121
|
-
LinkToolbar
|
|
122
|
-
toolbarMode: options.toolbarMode ?? "default",
|
|
123
|
-
}),
|
|
119
|
+
LinkToolbar,
|
|
124
120
|
ExitableMarks,
|
|
125
121
|
InsertParagraphAround,
|
|
126
122
|
TabIndent,
|
|
@@ -13,11 +13,6 @@ import { Extension } from "@tiptap/core";
|
|
|
13
13
|
import { Plugin, PluginKey } from "@tiptap/pm/state";
|
|
14
14
|
import type { EditorState } from "@tiptap/pm/state";
|
|
15
15
|
import type { EditorView } from "@tiptap/pm/view";
|
|
16
|
-
import {
|
|
17
|
-
applyDockedToolbarOffset,
|
|
18
|
-
isComposeDockedToolbar,
|
|
19
|
-
type FormattingToolbarMode,
|
|
20
|
-
} from "./toolbar-mode.js";
|
|
21
16
|
import {
|
|
22
17
|
getFixedFloatingContainerRect,
|
|
23
18
|
getFloatingPosition,
|
|
@@ -96,15 +91,8 @@ function getLinkRange(state: EditorState): LinkRange | null {
|
|
|
96
91
|
export const LinkToolbar = Extension.create({
|
|
97
92
|
name: "linkToolbar",
|
|
98
93
|
|
|
99
|
-
addOptions() {
|
|
100
|
-
return {
|
|
101
|
-
toolbarMode: "default" as FormattingToolbarMode,
|
|
102
|
-
};
|
|
103
|
-
},
|
|
104
|
-
|
|
105
94
|
addProseMirrorPlugins() {
|
|
106
95
|
const editor = this.editor;
|
|
107
|
-
const toolbarMode = this.options.toolbarMode as FormattingToolbarMode;
|
|
108
96
|
|
|
109
97
|
// DOM elements
|
|
110
98
|
let inputEl: HTMLElement | null = null;
|
|
@@ -204,17 +192,8 @@ export const LinkToolbar = Extension.create({
|
|
|
204
192
|
from: number,
|
|
205
193
|
to: number,
|
|
206
194
|
) {
|
|
207
|
-
const docked = isComposeDockedToolbar(toolbarMode);
|
|
208
|
-
el.classList.toggle("tiptap-link-input-docked", docked);
|
|
209
195
|
el.style.display = "flex";
|
|
210
196
|
|
|
211
|
-
if (docked) {
|
|
212
|
-
applyDockedToolbarOffset(el, view);
|
|
213
|
-
el.style.removeProperty("left");
|
|
214
|
-
el.style.removeProperty("top");
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
197
|
const dialog = view.dom.closest("dialog");
|
|
219
198
|
const start = view.coordsAtPos(from);
|
|
220
199
|
const end = view.coordsAtPos(to);
|
|
@@ -76,6 +76,47 @@ export const PasteMedia = Extension.create<PasteMediaOptions>({
|
|
|
76
76
|
addProseMirrorPlugins() {
|
|
77
77
|
const extension = this;
|
|
78
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Routes dropped/pasted files into inline images or attachments using the
|
|
81
|
+
* same decision as the host (`shouldInsertInline`). Returns false when
|
|
82
|
+
* there is nothing this extension can handle, so the caller leaves the
|
|
83
|
+
* event to the editor's default behavior.
|
|
84
|
+
*/
|
|
85
|
+
const routeFiles = (files: File[]): boolean => {
|
|
86
|
+
const inlineFiles = files.filter(
|
|
87
|
+
(file) => extension.options.shouldInsertInline?.(file) === true,
|
|
88
|
+
);
|
|
89
|
+
const attachmentFiles = files.filter(
|
|
90
|
+
(file) => !inlineFiles.includes(file),
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
if (
|
|
94
|
+
inlineFiles.length === 0 &&
|
|
95
|
+
(attachmentFiles.length === 0 ||
|
|
96
|
+
extension.options.onPasteFiles === undefined)
|
|
97
|
+
) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
for (const file of inlineFiles) {
|
|
102
|
+
const uploadInlineImage = extension.options.uploadInlineImage;
|
|
103
|
+
if (uploadInlineImage) {
|
|
104
|
+
void uploadInlineImage(file);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
void uploadAndInsertInlineImage(extension.editor, file);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (
|
|
111
|
+
attachmentFiles.length > 0 &&
|
|
112
|
+
extension.options.onPasteFiles !== undefined
|
|
113
|
+
) {
|
|
114
|
+
extension.options.onPasteFiles(attachmentFiles);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return true;
|
|
118
|
+
};
|
|
119
|
+
|
|
79
120
|
return [
|
|
80
121
|
new Plugin({
|
|
81
122
|
key: pasteMediaPluginKey,
|
|
@@ -83,40 +124,15 @@ export const PasteMedia = Extension.create<PasteMediaOptions>({
|
|
|
83
124
|
handlePaste(_view, event) {
|
|
84
125
|
const files = getClipboardFiles(event.clipboardData);
|
|
85
126
|
if (files.length === 0) return false;
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
if (
|
|
95
|
-
inlineFiles.length === 0 &&
|
|
96
|
-
(attachmentFiles.length === 0 ||
|
|
97
|
-
extension.options.onPasteFiles === undefined)
|
|
98
|
-
) {
|
|
99
|
-
return false;
|
|
100
|
-
}
|
|
101
|
-
|
|
127
|
+
if (!routeFiles(files)) return false;
|
|
128
|
+
event.preventDefault();
|
|
129
|
+
return true;
|
|
130
|
+
},
|
|
131
|
+
handleDrop(_view, event) {
|
|
132
|
+
const files = getClipboardFiles(event.dataTransfer);
|
|
133
|
+
if (files.length === 0) return false;
|
|
134
|
+
if (!routeFiles(files)) return false;
|
|
102
135
|
event.preventDefault();
|
|
103
|
-
|
|
104
|
-
for (const file of inlineFiles) {
|
|
105
|
-
const uploadInlineImage = extension.options.uploadInlineImage;
|
|
106
|
-
if (uploadInlineImage) {
|
|
107
|
-
void uploadInlineImage(file);
|
|
108
|
-
continue;
|
|
109
|
-
}
|
|
110
|
-
void uploadAndInsertInlineImage(extension.editor, file);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (
|
|
114
|
-
attachmentFiles.length > 0 &&
|
|
115
|
-
extension.options.onPasteFiles !== undefined
|
|
116
|
-
) {
|
|
117
|
-
extension.options.onPasteFiles(attachmentFiles);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
136
|
return true;
|
|
121
137
|
},
|
|
122
138
|
},
|
|
@@ -1,44 +1 @@
|
|
|
1
|
-
import type { EditorView } from "@tiptap/pm/view";
|
|
2
|
-
|
|
3
1
|
export type FormattingToolbarMode = "default" | "compose";
|
|
4
|
-
|
|
5
|
-
// Dock the formatting toolbar to the bottom of the compose surface only on
|
|
6
|
-
// touch-first devices. `(pointer: coarse)` targets the primary input, so real
|
|
7
|
-
// mobile/tablets match but a narrow desktop window (e.g. browser used as a
|
|
8
|
-
// sidebar) keeps the floating bubble menu near the selection.
|
|
9
|
-
const MOBILE_FORMATTING_QUERY = "(pointer: coarse)";
|
|
10
|
-
|
|
11
|
-
export function isComposeDockedToolbar(mode: FormattingToolbarMode): boolean {
|
|
12
|
-
return (
|
|
13
|
-
mode === "compose" &&
|
|
14
|
-
(globalThis.matchMedia?.(MOBILE_FORMATTING_QUERY).matches ?? false)
|
|
15
|
-
);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function applyDockedToolbarOffset(
|
|
19
|
-
el: HTMLElement,
|
|
20
|
-
view: EditorView,
|
|
21
|
-
): void {
|
|
22
|
-
const composeEditor = view.dom.closest("jant-compose-editor");
|
|
23
|
-
const root =
|
|
24
|
-
view.dom.closest(
|
|
25
|
-
"jant-compose-dialog, .compose-fullscreen, .compose-attached-panel",
|
|
26
|
-
) ?? view.dom.closest("dialog");
|
|
27
|
-
|
|
28
|
-
let offset = 16;
|
|
29
|
-
const toolsRow =
|
|
30
|
-
composeEditor?.querySelector<HTMLElement>(".compose-tools-row") ??
|
|
31
|
-
root?.querySelector<HTMLElement>(".compose-tools-row");
|
|
32
|
-
const actionRow = root?.querySelector<HTMLElement>(".compose-action-row");
|
|
33
|
-
const attachmentDock = composeEditor?.querySelector<HTMLElement>(
|
|
34
|
-
".compose-attachments-dock",
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
if (toolsRow) offset += toolsRow.getBoundingClientRect().height;
|
|
38
|
-
if (actionRow) offset += actionRow.getBoundingClientRect().height;
|
|
39
|
-
if (attachmentDock) {
|
|
40
|
-
offset += attachmentDock.getBoundingClientRect().height;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
el.style.setProperty("--tiptap-docked-offset", `${offset}px`);
|
|
44
|
-
}
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
* - Strips spurious rotation metadata from the output (mediabunny may
|
|
8
8
|
* bake rotation into pixels AND write a display matrix, causing the
|
|
9
9
|
* browser to double-rotate)
|
|
10
|
+
* - Clears the alternate_group track flag (mediabunny sets it non-zero,
|
|
11
|
+
* which stops Safari's native video controls from auto-hiding)
|
|
10
12
|
* - Extracts poster frame + blurhash during processing
|
|
11
13
|
*
|
|
12
14
|
* Requires WebCodecs API support — check `isSupported()` before use.
|
|
@@ -25,6 +27,7 @@ import {
|
|
|
25
27
|
} from "mediabunny";
|
|
26
28
|
import { encode } from "blurhash";
|
|
27
29
|
import { normalizeDurationSeconds } from "../lib/video-playback.js";
|
|
30
|
+
import { zeroTrackAlternateGroups } from "../lib/mp4-track-flags.js";
|
|
28
31
|
|
|
29
32
|
/** Maximum pixels for the long edge of the output video. */
|
|
30
33
|
const MAX_LONG_EDGE = 1920;
|
|
@@ -222,6 +225,12 @@ async function processToFile(
|
|
|
222
225
|
const buffer = target.buffer;
|
|
223
226
|
if (!buffer) throw new Error("Video processing produced no output");
|
|
224
227
|
|
|
228
|
+
// Mediabunny tags each track with a non-zero alternate_group, which makes
|
|
229
|
+
// Safari treat tracks as mutually exclusive alternates and never auto-hide
|
|
230
|
+
// the native <video> control bar during playback. Zero it so the controls
|
|
231
|
+
// behave like any other MP4.
|
|
232
|
+
zeroTrackAlternateGroups(buffer);
|
|
233
|
+
|
|
225
234
|
// Detect whether this browser double-rotates. Chrome's WebCodecs
|
|
226
235
|
// bakes rotation into the pixel data AND mediabunny writes a display
|
|
227
236
|
// matrix → the browser applies the matrix again (double-rotation).
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
CREATE TABLE `telegram_binding` (
|
|
2
|
+
`id` text PRIMARY KEY NOT NULL,
|
|
3
|
+
`site_id` text NOT NULL,
|
|
4
|
+
`bot_id` text NOT NULL,
|
|
5
|
+
`telegram_user_id` text NOT NULL,
|
|
6
|
+
`telegram_username` text,
|
|
7
|
+
`last_update_id` integer,
|
|
8
|
+
`bound_at` integer NOT NULL,
|
|
9
|
+
FOREIGN KEY (`site_id`) REFERENCES `site`(`id`) ON UPDATE no action ON DELETE cascade
|
|
10
|
+
);
|
|
11
|
+
--> statement-breakpoint
|
|
12
|
+
CREATE UNIQUE INDEX `uq_telegram_binding_site_id` ON `telegram_binding` (`site_id`);--> statement-breakpoint
|
|
13
|
+
CREATE UNIQUE INDEX `uq_telegram_binding_bot_user` ON `telegram_binding` (`bot_id`,`telegram_user_id`);--> statement-breakpoint
|
|
14
|
+
CREATE TABLE `telegram_pending_binding` (
|
|
15
|
+
`id` text PRIMARY KEY NOT NULL,
|
|
16
|
+
`site_id` text NOT NULL,
|
|
17
|
+
`code` text NOT NULL,
|
|
18
|
+
`created_at` integer NOT NULL,
|
|
19
|
+
`expires_at` integer NOT NULL,
|
|
20
|
+
FOREIGN KEY (`site_id`) REFERENCES `site`(`id`) ON UPDATE no action ON DELETE cascade
|
|
21
|
+
);
|
|
22
|
+
--> statement-breakpoint
|
|
23
|
+
CREATE UNIQUE INDEX `uq_telegram_pending_binding_site_id` ON `telegram_pending_binding` (`site_id`);--> statement-breakpoint
|
|
24
|
+
CREATE UNIQUE INDEX `uq_telegram_pending_binding_code` ON `telegram_pending_binding` (`code`);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
CREATE TABLE `telegram_media_group_item` (
|
|
2
|
+
`id` text PRIMARY KEY NOT NULL,
|
|
3
|
+
`site_id` text NOT NULL,
|
|
4
|
+
`bot_id` text NOT NULL,
|
|
5
|
+
`telegram_user_id` text NOT NULL,
|
|
6
|
+
`media_group_id` text NOT NULL,
|
|
7
|
+
`chat_id` integer NOT NULL,
|
|
8
|
+
`message_id` integer NOT NULL,
|
|
9
|
+
`update_id` integer NOT NULL,
|
|
10
|
+
`file_id` text NOT NULL,
|
|
11
|
+
`media_kind` text NOT NULL,
|
|
12
|
+
`mime_type` text,
|
|
13
|
+
`original_name` text,
|
|
14
|
+
`caption_markdown` text,
|
|
15
|
+
`created_at` integer NOT NULL,
|
|
16
|
+
FOREIGN KEY (`site_id`) REFERENCES `site`(`id`) ON UPDATE no action ON DELETE cascade
|
|
17
|
+
);
|
|
18
|
+
--> statement-breakpoint
|
|
19
|
+
CREATE INDEX `idx_telegram_media_group_item_group` ON `telegram_media_group_item` (`bot_id`,`media_group_id`);--> statement-breakpoint
|
|
20
|
+
CREATE UNIQUE INDEX `uq_telegram_media_group_item_message` ON `telegram_media_group_item` (`bot_id`,`media_group_id`,`message_id`);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ALTER TABLE `telegram_media_group_item` ADD `poster_file_id` text;
|