@yaebal/preview 0.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 neverlane
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # @yaebal/preview
2
+
3
+ > ⚠️ **experimental / wip.** api and rendering may change without notice. not ready for production.
4
+
5
+ render a telegram-style chat from plain objects to an **SVG string** — rich text, every
6
+ common media type, the lot. zero runtime, no `<foreignObject>` (so it rasterizes and
7
+ survives github's SVG sanitizer). media fields use the real `@yaebal/types` shapes, so you
8
+ can hand it a `ctx.message` almost verbatim. drop the result into docs, a README, or a
9
+ landing page.
10
+
11
+ ## install
12
+
13
+ ```sh
14
+ pnpm add @yaebal/preview
15
+ ```
16
+
17
+ ## use
18
+
19
+ ```ts
20
+ import { renderChat } from "@yaebal/preview";
21
+ import { md } from "@yaebal/fmt"; // optional — produces { text, entities }
22
+
23
+ const svg = renderChat(
24
+ [
25
+ { from: "user", text: "/start", time: "23:33", status: "read" },
26
+ { from: "bot", name: "yaebal", ...md`Hello, **unknown** person`, time: "23:33" },
27
+ { from: "bot", name: "yaebal", photo: [], src: "cat.jpg", caption: "a cat" },
28
+ { from: "bot", name: "yaebal", voice: { duration: 7 } },
29
+ { from: "bot", name: "yaebal", buttons: [["Useless button"]] },
30
+ ],
31
+ { theme: "light", width: 400 },
32
+ );
33
+
34
+ await writeFile("chat.svg", svg); // it's just a string
35
+ ```
36
+
37
+ ## messages
38
+
39
+ | field | meaning |
40
+ |:---------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------|
41
+ | `from` | `"user"` → outgoing (right, ticks) · `"bot"` → incoming (left, avatar) |
42
+ | `text` | message text — wrapped automatically |
43
+ | `entities` | telegram `MessageEntity[]` — bold/italic/underline/strike/code/spoiler/link. spread `@yaebal/fmt`'s `md`/`html` to get these for free |
44
+ | `caption` / `captionEntities` | text under a media message |
45
+ | `time` · `status` · `buttons` · `name` | time · ticks (`sent`/`delivered`/`read`) · keyboard rows · sender label + avatar |
46
+
47
+ ## media
48
+
49
+ all use the real `@yaebal/types` shapes. for picture-like media add `src` (a URL/data-URI)
50
+ to show real pixels — a `file_id` has none, so without `src` you get a clean placeholder.
51
+
52
+ | field | renders as |
53
+ |:---------------------|:------------------------------------------------------|
54
+ | `photo` | image (or placeholder) + optional `caption` |
55
+ | `sticker` | standalone image, or its `emoji` big |
56
+ | `animation` | image + `GIF` badge |
57
+ | `video` | image + play button + duration |
58
+ | `voice` | waveform + duration |
59
+ | `audio` | play disc + title / performer |
60
+ | `document` | file icon + name + size |
61
+ | `venue` / `location` | map tile + pin (+ title/address) |
62
+ | `contact` | avatar + name + phone |
63
+ | `poll` | question + options with percentage bars |
64
+
65
+ add `spoiler: true` to cover picture media.
66
+
67
+ ## options
68
+
69
+ | option | default | meaning |
70
+ |:---------|:-------------|:----------------------------------------|
71
+ | `theme` | `"light"` | `"light"` (green wallpaper) or `"dark"` |
72
+ | `width` | `380` | canvas width in px |
73
+ | `avatar` | name initial | override the incoming avatar glyph |
74
+
75
+ ---
76
+
77
+ part of [**yaebal**](https://github.com/neverlane/yaebal) — a type-safe, runtime-agnostic Telegram Bot API framework. MIT.
package/lib/index.d.ts ADDED
@@ -0,0 +1,64 @@
1
+ /**
2
+ * @yaebal/preview — render a telegram-style chat from plain objects to an SVG string.
3
+ * zero runtime, no `<foreignObject>` (so it rasterizes and survives github's SVG
4
+ * sanitizer). media fields use the real `@yaebal/types` shapes, so you can hand it a
5
+ * `ctx.message` almost verbatim; add `src` to show real pixels (a `file_id` has none).
6
+ *
7
+ * import { renderChat } from "@yaebal/preview";
8
+ * import { md } from "@yaebal/fmt";
9
+ *
10
+ * renderChat([
11
+ * { from: "user", text: "/start", time: "23:33", status: "read" },
12
+ * { from: "bot", name: "yaebal", ...md`hello, **unknown** person`, time: "23:33" },
13
+ * { from: "bot", name: "yaebal", photo: [], src: "cat.jpg", caption: "a cat" },
14
+ * { from: "bot", name: "yaebal", voice: { duration: 7 } },
15
+ * { from: "bot", name: "yaebal", poll: { question: "tabs?", options: [...] } },
16
+ * ], { theme: "light" });
17
+ */
18
+ import type { Animation, Audio, Contact, Document, Location, MessageEntity, PhotoSize, Poll, Sticker, Venue, Video, Voice } from "@yaebal/types";
19
+ export type Side = "user" | "bot";
20
+ export type TickStatus = "sent" | "delivered" | "read";
21
+ export interface ChatMessage {
22
+ from: Side;
23
+ /** sender label (incoming); also drives the avatar initial + colour. */
24
+ name?: string;
25
+ time?: string;
26
+ /** outgoing read receipt (ticks). ignored for incoming. */
27
+ status?: TickStatus;
28
+ /** keyboard rows rendered as buttons under the message. */
29
+ buttons?: string[][];
30
+ /** message text. spread `@yaebal/fmt`'s `md`/`html` to also pass `entities`. */
31
+ text?: string;
32
+ /** telegram entities for `text` (bold/italic/code/link/spoiler/…). */
33
+ entities?: MessageEntity[];
34
+ /** caption for a media message. */
35
+ caption?: string;
36
+ /** telegram entities for `caption`. */
37
+ captionEntities?: MessageEntity[];
38
+ /** real image/thumb URL or data-URI for the picture-like media below (a `file_id` can't render). */
39
+ src?: string;
40
+ /** cover the media with a spoiler. */
41
+ spoiler?: boolean;
42
+ photo?: PhotoSize[];
43
+ sticker?: Sticker;
44
+ animation?: Animation;
45
+ video?: Video;
46
+ voice?: Voice;
47
+ audio?: Audio;
48
+ document?: Document;
49
+ venue?: Venue;
50
+ location?: Location;
51
+ contact?: Contact;
52
+ poll?: Poll;
53
+ }
54
+ export interface RenderOptions {
55
+ /** default `"light"` (the green-wallpaper look). */
56
+ theme?: "dark" | "light";
57
+ /** canvas width in px. defaults to `380`. */
58
+ width?: number;
59
+ /** override the avatar glyph for incoming messages (else the name's initial). */
60
+ avatar?: string;
61
+ }
62
+ /** render a telegram-style chat to an SVG string. */
63
+ export declare function renderChat(messages: ChatMessage[], options?: RenderOptions): string;
64
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EACX,SAAS,EACT,KAAK,EACL,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,aAAa,EACb,SAAS,EACT,IAAI,EACJ,OAAO,EACP,KAAK,EACL,KAAK,EACL,KAAK,EACL,MAAM,eAAe,CAAC;AAEvB,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,KAAK,CAAC;AAClC,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC;AAEvD,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,IAAI,CAAC;IACX,wEAAwE;IACxE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC;IAErB,gFAAgF;IAChF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sEAAsE;IACtE,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;IAC3B,mCAAmC;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,eAAe,CAAC,EAAE,aAAa,EAAE,CAAC;IAElC,oGAAoG;IACpG,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,sCAAsC;IACtC,OAAO,CAAC,EAAE,OAAO,CAAC;IAGlB,KAAK,CAAC,EAAE,SAAS,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,IAAI,CAAC;CACZ;AAED,MAAM,WAAW,aAAa;IAC7B,oDAAoD;IACpD,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AA6cD,qDAAqD;AACrD,wBAAgB,UAAU,CAAC,QAAQ,EAAE,WAAW,EAAE,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CAiVvF"}