tgwidget 0.1.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/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # tgwidget
2
+
3
+ Node.js SDK for [TeleWidget](https://tgwidget.github.io/) — beautiful Telegram Mini App widgets for bots.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install tgwidget
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Generate widget URL
14
+
15
+ ```typescript
16
+ import { tgwidget } from "tgwidget";
17
+
18
+ // Date picker
19
+ const url = tgwidget("your_bot").date({ mode: "datetime", format: "unix-s" }).url();
20
+
21
+ // Color picker
22
+ const url = tgwidget("your_bot").color({ format: "hex" }).url();
23
+
24
+ // Schedule
25
+ const url = tgwidget("your_bot").schedule().url();
26
+
27
+ // With styling
28
+ const url = tgwidget("your_bot")
29
+ .date({ mode: "date" })
30
+ .style({ colorScheme: "dark", accent: "#FF6600", adoptTgPalette: true })
31
+ .url();
32
+ ```
33
+
34
+ ### Parse results
35
+
36
+ When a user completes the widget, the result comes back via deep link `t.me/your_bot?start=VALUE`. Parse the value:
37
+
38
+ ```typescript
39
+ import { parseDate, parseColor, parseSchedule } from "tgwidget";
40
+
41
+ // Date result
42
+ const result = parseDate("2025-03-15", { mode: "date" });
43
+ // { date: '2025-03-15' }
44
+
45
+ // Unix timestamp
46
+ const result = parseDate("1710460800_1718236800", { mode: "date-range", format: "unix-s" });
47
+ // { timestamp: 1710460800, timestampEnd: 1718236800, date: '2025-03-15', ... }
48
+
49
+ // Color
50
+ const result = parseColor("FF6600", { format: "hex" });
51
+ // { raw: 'FF6600', hex: '#FF6600' }
52
+
53
+ // Schedule (56-char bunch format)
54
+ const result = parseSchedule("09001800090018000000000009001800090018000000000000000000");
55
+ // [{ enabled: true, start: '09:00', end: '18:00' }, ...]
56
+ ```
57
+
58
+ ## API
59
+
60
+ ### `tgwidget(botUsername)`
61
+
62
+ Create a widget builder. Returns a chainable `TgWidget` instance.
63
+
64
+ - `.date({ mode?, format?, order? })` — Date/time picker
65
+ - `.color({ format? })` — Color picker
66
+ - `.schedule()` — Weekly schedule
67
+ - `.style({ colorScheme?, accent?, tint?, liquidGlass?, adaptTgTheme?, adoptTgPalette? })` — Styling
68
+ - `.url(baseUrl?)` — Generate the final URL
69
+ - `.payload()` — Get the raw payload object
70
+
71
+ ### Parsers
72
+
73
+ - `parseDate(value, { mode?, format?, order? })` → `DateResult`
74
+ - `parseColor(value, { format? })` → `ColorResult`
75
+ - `parseSchedule(value)` → `ScheduleDay[]`
@@ -0,0 +1,29 @@
1
+ import type { DateMode, DateFormat, DateOrder, ColorFormat, ColorScheme } from "./types";
2
+ export declare class TgWidget {
3
+ private _botUsername;
4
+ private _widget;
5
+ private _payload;
6
+ private _style;
7
+ constructor(botUsername: string);
8
+ date(opts?: {
9
+ mode?: DateMode;
10
+ format?: DateFormat;
11
+ order?: DateOrder;
12
+ }): this;
13
+ color(opts?: {
14
+ format?: ColorFormat;
15
+ }): this;
16
+ schedule(): this;
17
+ style(opts?: {
18
+ colorScheme?: ColorScheme;
19
+ accent?: string;
20
+ tint?: string;
21
+ liquidGlass?: boolean;
22
+ adaptTgTheme?: boolean;
23
+ adoptTgPalette?: boolean;
24
+ }): this;
25
+ private _buildPayload;
26
+ url(baseUrl?: string): string;
27
+ payload(): Record<string, unknown>;
28
+ }
29
+ export declare function tgwidget(botUsername: string): TgWidget;
@@ -0,0 +1,89 @@
1
+ const BASE_URL = "https://tgwidget.github.io/";
2
+ const HEX_RE = /^#?[0-9A-Fa-f]{6}$/;
3
+ const VALID_DATE_MODES = new Set(["date", "time", "datetime", "date-range", "time-range"]);
4
+ const VALID_DATE_FORMATS = new Set(["default", "unix-s", "unix-ms"]);
5
+ const VALID_DATE_ORDERS = new Set(["ymd", "dmy", "mdy"]);
6
+ const VALID_COLOR_FORMATS = new Set(["hex", "rgb", "hsl"]);
7
+ const VALID_COLOR_SCHEMES = new Set(["light", "dark", "auto"]);
8
+ function validateHex(color, name) {
9
+ if (!HEX_RE.test(color)) {
10
+ throw new Error(`${name} must be a valid hex color (e.g. '#FF0000'), got '${color}'`);
11
+ }
12
+ return color.startsWith("#") ? color : `#${color}`;
13
+ }
14
+ function toBase64(str) {
15
+ if (typeof Buffer !== "undefined") {
16
+ return Buffer.from(str).toString("base64");
17
+ }
18
+ return btoa(str);
19
+ }
20
+ export class TgWidget {
21
+ constructor(botUsername) {
22
+ this._widget = null;
23
+ this._payload = {};
24
+ this._style = {};
25
+ if (!botUsername)
26
+ throw new Error("botUsername is required");
27
+ this._botUsername = botUsername;
28
+ }
29
+ date(opts = {}) {
30
+ const { mode = "date", format = "default", order = "ymd" } = opts;
31
+ if (!VALID_DATE_MODES.has(mode))
32
+ throw new Error(`Invalid date mode '${mode}'`);
33
+ if (!VALID_DATE_FORMATS.has(format))
34
+ throw new Error(`Invalid date format '${format}'`);
35
+ if (!VALID_DATE_ORDERS.has(order))
36
+ throw new Error(`Invalid date order '${order}'`);
37
+ this._widget = "date";
38
+ this._payload = { mode, format, order };
39
+ return this;
40
+ }
41
+ color(opts = {}) {
42
+ const { format = "hex" } = opts;
43
+ if (!VALID_COLOR_FORMATS.has(format))
44
+ throw new Error(`Invalid color format '${format}'`);
45
+ this._widget = "color";
46
+ this._payload = { format };
47
+ return this;
48
+ }
49
+ schedule() {
50
+ this._widget = "schedule";
51
+ this._payload = { format: "bunch" };
52
+ return this;
53
+ }
54
+ style(opts = {}) {
55
+ const { colorScheme = "light", accent, tint, liquidGlass = false, adaptTgTheme = false, adoptTgPalette = false } = opts;
56
+ if (!VALID_COLOR_SCHEMES.has(colorScheme))
57
+ throw new Error(`Invalid colorScheme '${colorScheme}'`);
58
+ this._style = { colorScheme, liquidGlass, adaptTgTheme, adoptTgPalette };
59
+ if (accent)
60
+ this._style.accent = validateHex(accent, "accent");
61
+ if (tint)
62
+ this._style.tint = validateHex(tint, "tint");
63
+ return this;
64
+ }
65
+ _buildPayload() {
66
+ if (!this._widget)
67
+ throw new Error("No widget type set. Call .date(), .color(), or .schedule() first.");
68
+ const payload = {
69
+ widget: this._widget,
70
+ bot_username: this._botUsername,
71
+ ...this._payload,
72
+ };
73
+ if (Object.keys(this._style).length > 0) {
74
+ payload.style = this._style;
75
+ }
76
+ return payload;
77
+ }
78
+ url(baseUrl = BASE_URL) {
79
+ const payload = this._buildPayload();
80
+ const encoded = toBase64(JSON.stringify(payload));
81
+ return `${baseUrl}?p=${encoded}`;
82
+ }
83
+ payload() {
84
+ return this._buildPayload();
85
+ }
86
+ }
87
+ export function tgwidget(botUsername) {
88
+ return new TgWidget(botUsername);
89
+ }
@@ -0,0 +1,3 @@
1
+ export { tgwidget, TgWidget } from "./builder";
2
+ export { parseDate, parseColor, parseSchedule } from "./parser";
3
+ export type { DateMode, DateFormat, DateOrder, ColorFormat, ColorScheme, WidgetStyle, DateResult, ColorResult, ScheduleDay, } from "./types";
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { tgwidget, TgWidget } from "./builder";
2
+ export { parseDate, parseColor, parseSchedule } from "./parser";
@@ -0,0 +1,10 @@
1
+ import type { DateMode, DateFormat, DateOrder, ColorFormat, DateResult, ColorResult, ScheduleDay } from "./types";
2
+ export declare function parseDate(value: string, opts?: {
3
+ mode?: DateMode;
4
+ format?: DateFormat;
5
+ order?: DateOrder;
6
+ }): DateResult;
7
+ export declare function parseColor(value: string, opts?: {
8
+ format?: ColorFormat;
9
+ }): ColorResult;
10
+ export declare function parseSchedule(value: string): ScheduleDay[];
package/dist/parser.js ADDED
@@ -0,0 +1,97 @@
1
+ function parseDateStr(value, order) {
2
+ const parts = value.split("-");
3
+ if (order === "ymd")
4
+ return `${parts[0]}-${parts[1]}-${parts[2]}`;
5
+ if (order === "dmy")
6
+ return `${parts[2]}-${parts[1]}-${parts[0]}`;
7
+ return `${parts[2]}-${parts[0]}-${parts[1]}`; // mdy
8
+ }
9
+ export function parseDate(value, opts = {}) {
10
+ const { mode = "date", format = "default", order = "ymd" } = opts;
11
+ const result = {};
12
+ if (format === "unix-s" || format === "unix-ms") {
13
+ const parts = value.split("_");
14
+ const mul = format === "unix-s" ? 1000 : 1;
15
+ const ts = parseInt(parts[0], 10);
16
+ result.timestamp = ts;
17
+ const dt = new Date(ts * mul);
18
+ result.date = dt.toISOString().slice(0, 10);
19
+ result.time = dt.toISOString().slice(11, 16);
20
+ if (parts.length > 1) {
21
+ const tsEnd = parseInt(parts[1], 10);
22
+ result.timestampEnd = tsEnd;
23
+ const dtEnd = new Date(tsEnd * mul);
24
+ result.dateEnd = dtEnd.toISOString().slice(0, 10);
25
+ result.timeEnd = dtEnd.toISOString().slice(11, 16);
26
+ }
27
+ return result;
28
+ }
29
+ switch (mode) {
30
+ case "date":
31
+ result.date = parseDateStr(value, order);
32
+ break;
33
+ case "time": {
34
+ const [h, m] = value.split("-");
35
+ result.time = `${h}:${m}`;
36
+ break;
37
+ }
38
+ case "datetime": {
39
+ const [datePart, timePart] = value.split("_");
40
+ result.date = parseDateStr(datePart, order);
41
+ const [h, m] = timePart.split("-");
42
+ result.time = `${h}:${m}`;
43
+ break;
44
+ }
45
+ case "date-range": {
46
+ const parts = value.split("_");
47
+ result.date = parseDateStr(parts[0], order);
48
+ result.dateEnd = parseDateStr(parts[1], order);
49
+ break;
50
+ }
51
+ case "time-range": {
52
+ const parts = value.split("_");
53
+ const [h1, m1] = parts[0].split("-");
54
+ const [h2, m2] = parts[1].split("-");
55
+ result.time = `${h1}:${m1}`;
56
+ result.timeEnd = `${h2}:${m2}`;
57
+ break;
58
+ }
59
+ }
60
+ return result;
61
+ }
62
+ export function parseColor(value, opts = {}) {
63
+ const { format = "hex" } = opts;
64
+ const result = { raw: value };
65
+ if (format === "hex") {
66
+ result.hex = `#${value}`;
67
+ }
68
+ else if (format === "rgb") {
69
+ const parts = value.split("_").map(Number);
70
+ result.rgb = [parts[0], parts[1], parts[2]];
71
+ }
72
+ else if (format === "hsl") {
73
+ const parts = value.split("_").map(Number);
74
+ result.hsl = [parts[0], parts[1], parts[2]];
75
+ }
76
+ return result;
77
+ }
78
+ export function parseSchedule(value) {
79
+ if (value.length !== 56) {
80
+ throw new Error(`Schedule bunch format must be 56 chars, got ${value.length}`);
81
+ }
82
+ const days = [];
83
+ for (let i = 0; i < 7; i++) {
84
+ const chunk = value.slice(i * 8, (i + 1) * 8);
85
+ if (chunk === "00000000") {
86
+ days.push({ enabled: false });
87
+ }
88
+ else {
89
+ days.push({
90
+ enabled: true,
91
+ start: `${chunk.slice(0, 2)}:${chunk.slice(2, 4)}`,
92
+ end: `${chunk.slice(4, 6)}:${chunk.slice(6, 8)}`,
93
+ });
94
+ }
95
+ }
96
+ return days;
97
+ }
@@ -0,0 +1,32 @@
1
+ export type DateMode = "date" | "time" | "datetime" | "date-range" | "time-range";
2
+ export type DateFormat = "default" | "unix-s" | "unix-ms";
3
+ export type DateOrder = "ymd" | "dmy" | "mdy";
4
+ export type ColorFormat = "hex" | "rgb" | "hsl";
5
+ export type ColorScheme = "light" | "dark" | "auto";
6
+ export interface WidgetStyle {
7
+ colorScheme?: ColorScheme;
8
+ accent?: string;
9
+ tint?: string;
10
+ liquidGlass?: boolean;
11
+ adaptTgTheme?: boolean;
12
+ adoptTgPalette?: boolean;
13
+ }
14
+ export interface DateResult {
15
+ date?: string;
16
+ time?: string;
17
+ dateEnd?: string;
18
+ timeEnd?: string;
19
+ timestamp?: number;
20
+ timestampEnd?: number;
21
+ }
22
+ export interface ColorResult {
23
+ raw: string;
24
+ hex?: string;
25
+ rgb?: [number, number, number];
26
+ hsl?: [number, number, number];
27
+ }
28
+ export interface ScheduleDay {
29
+ enabled: boolean;
30
+ start?: string;
31
+ end?: string;
32
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "tgwidget",
3
+ "version": "0.1.0",
4
+ "description": "Node.js SDK for TeleWidget — Telegram Mini App widgets",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": ["dist"],
15
+ "scripts": {
16
+ "build": "tsc",
17
+ "prepublishOnly": "tsc"
18
+ },
19
+ "keywords": ["telegram", "mini-apps", "widget", "bot", "tgwidget"],
20
+ "license": "MIT",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/tgwidget/tgwidget-node"
24
+ },
25
+ "homepage": "https://github.com/tgwidget/tgwidget-node",
26
+ "devDependencies": {
27
+ "@types/node": "^25.6.2",
28
+ "typescript": "^5.0.0"
29
+ }
30
+ }