@yaebal/morda 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,13 @@
1
+ # @yaebal/morda
2
+
3
+ context inside a dialog: the base context plus the navigation control.
4
+
5
+ ## install
6
+
7
+ ```sh
8
+ pnpm add @yaebal/morda
9
+ ```
10
+
11
+ ---
12
+
13
+ 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,69 @@
1
+ import type { Context, Plugin } from "@yaebal/core";
2
+ import { type StorageAdapter } from "@yaebal/session";
3
+ /** context inside a dialog: the base context plus the navigation control. */
4
+ export type DialogContext = Context & {
5
+ dialog: DialogControl;
6
+ };
7
+ /** a single button. `onClick` may navigate via `ctx.dialog`. */
8
+ export interface Button {
9
+ id: string;
10
+ label: string;
11
+ onClick?: (ctx: DialogContext) => unknown;
12
+ }
13
+ /** what a window renders to: a message text and rows of buttons. */
14
+ export interface WindowView {
15
+ text: string;
16
+ keyboard?: Button[][];
17
+ }
18
+ /** a window is rendered on demand, so its text/buttons can depend on context. */
19
+ export type WindowRender = (ctx: DialogContext) => WindowView | Promise<WindowView>;
20
+ /** a dialog is a flat map of named windows. */
21
+ export type DialogDef = Record<string, WindowRender>;
22
+ /** navigation control exposed as `ctx.dialog`. */
23
+ export interface DialogControl {
24
+ /** open a window in a fresh message + stack. */
25
+ start(windowId: string): Promise<void>;
26
+ /** push a window onto the stack and edit the message. */
27
+ push(windowId: string): Promise<void>;
28
+ /** replace the top window and edit the message. */
29
+ replace(windowId: string): Promise<void>;
30
+ /** pop the stack; closing the dialog (deleting the message) at the root. */
31
+ back(): Promise<void>;
32
+ /** re-render the current window in place (after mutating external state). */
33
+ rerender(): Promise<void>;
34
+ }
35
+ /** persisted per-chat navigation state. */
36
+ export interface DialogState {
37
+ stack: string[];
38
+ messageId: number;
39
+ chatId: number;
40
+ }
41
+ export interface DialogsOptions {
42
+ /** where to persist navigation state. defaults to in-memory (lost on restart). */
43
+ storage?: StorageAdapter<DialogState>;
44
+ /** fired when a window leaves the stack (popped, replaced, or dialog restarted).
45
+ * the JSX layer uses this to drop a window's hook state so it re-mounts fresh. */
46
+ onLeave?: (chatId: number, windowId: string) => void;
47
+ }
48
+ /** A button that navigates to another window. */
49
+ export declare function switchTo(label: string, windowId: string): Button;
50
+ /** A button that pops the stack. */
51
+ export declare function back(label?: string): Button;
52
+ /** A plain action button. */
53
+ export declare function button(label: string, opts: {
54
+ id: string;
55
+ onClick?: (ctx: DialogContext) => unknown;
56
+ }): Button;
57
+ /**
58
+ * Dialogs plugin. Renders declarative windows, routes button presses by stable
59
+ * id, and maintains a per-chat navigation stack — no manual `editMessageText`
60
+ * or callback_data wrangling.
61
+ *
62
+ * ponytail: builder-API has no auto re-render on state change — call
63
+ * `ctx.dialog.rerender()` after mutating data a window reads. The JSX/hooks
64
+ * layer (M2b) automates this via `useState`.
65
+ */
66
+ export declare function dialogs(def: DialogDef, options?: DialogsOptions): Plugin<Context, {
67
+ dialog: DialogControl;
68
+ }>;
69
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEpD,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAErE,6EAA6E;AAC7E,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG;IAAE,MAAM,EAAE,aAAa,CAAA;CAAE,CAAC;AAEhE,gEAAgE;AAChE,MAAM,WAAW,MAAM;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC;CAC1C;AAED,oEAAoE;AACpE,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC;CACtB;AAED,iFAAiF;AACjF,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,aAAa,KAAK,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEpF,+CAA+C;AAC/C,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AAErD,kDAAkD;AAClD,MAAM,WAAW,aAAa;IAC7B,gDAAgD;IAChD,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,yDAAyD;IACzD,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,mDAAmD;IACnD,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,4EAA4E;IAC5E,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,6EAA6E;IAC7E,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,2CAA2C;AAC3C,MAAM,WAAW,WAAW;IAC3B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC9B,kFAAkF;IAClF,OAAO,CAAC,EAAE,cAAc,CAAC,WAAW,CAAC,CAAC;IACtC;uFACmF;IACnF,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;AAED,iDAAiD;AACjD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED,oCAAoC;AACpC,wBAAgB,IAAI,CAAC,KAAK,SAAY,GAAG,MAAM,CAE9C;AAED,6BAA6B;AAC7B,wBAAgB,MAAM,CACrB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,CAAA;CAAE,GAC7D,MAAM,CAER;AASD;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CACtB,GAAG,EAAE,SAAS,EACd,OAAO,GAAE,cAAmB,GAC1B,MAAM,CAAC,OAAO,EAAE;IAAE,MAAM,EAAE,aAAa,CAAA;CAAE,CAAC,CAqH5C"}
package/lib/index.js ADDED
@@ -0,0 +1,156 @@
1
+ import { callbackData } from "@yaebal/callback-data";
2
+ import { InlineKeyboard } from "@yaebal/keyboard";
3
+ import { MemoryStorage } from "@yaebal/session";
4
+ /** A button that navigates to another window. */
5
+ export function switchTo(label, windowId) {
6
+ return { id: `to:${windowId}`, label, onClick: (ctx) => ctx.dialog.push(windowId) };
7
+ }
8
+ /** A button that pops the stack. */
9
+ export function back(label = "← Назад") {
10
+ return { id: "back", label, onClick: (ctx) => ctx.dialog.back() };
11
+ }
12
+ /** A plain action button. */
13
+ export function button(label, opts) {
14
+ return { id: opts.id, label, onClick: opts.onClick };
15
+ }
16
+ function findButton(view, id) {
17
+ for (const row of view.keyboard ?? []) {
18
+ for (const b of row)
19
+ if (b.id === id)
20
+ return b;
21
+ }
22
+ return undefined;
23
+ }
24
+ /**
25
+ * Dialogs plugin. Renders declarative windows, routes button presses by stable
26
+ * id, and maintains a per-chat navigation stack — no manual `editMessageText`
27
+ * or callback_data wrangling.
28
+ *
29
+ * ponytail: builder-API has no auto re-render on state change — call
30
+ * `ctx.dialog.rerender()` after mutating data a window reads. The JSX/hooks
31
+ * layer (M2b) automates this via `useState`.
32
+ */
33
+ export function dialogs(def, options = {}) {
34
+ const storage = options.storage ?? new MemoryStorage();
35
+ const cd = callbackData("dlg", { w: String, b: String });
36
+ const renderKeyboard = (windowId, view) => {
37
+ const kb = new InlineKeyboard();
38
+ for (const row of view.keyboard ?? []) {
39
+ for (const b of row)
40
+ kb.text(b.label, cd.pack({ w: windowId, b: b.id }));
41
+ kb.row();
42
+ }
43
+ return kb.build();
44
+ };
45
+ const renderWindow = async (w, ctx) => {
46
+ const window = def[w];
47
+ if (!window)
48
+ throw new Error(`morda: unknown window "${w}"`);
49
+ return window(ctx);
50
+ };
51
+ const control = (ctx) => {
52
+ const dctx = ctx;
53
+ const chatId = ctx.chat?.id;
54
+ const key = chatId?.toString();
55
+ const load = async () => key === undefined ? undefined : storage.get(key);
56
+ const edit = async (st) => {
57
+ const w = st.stack[st.stack.length - 1];
58
+ if (w === undefined || key === undefined)
59
+ return;
60
+ const view = await renderWindow(w, dctx);
61
+ await ctx.api.call("editMessageText", {
62
+ chat_id: st.chatId,
63
+ message_id: st.messageId,
64
+ text: view.text,
65
+ reply_markup: renderKeyboard(w, view),
66
+ });
67
+ await storage.set(key, st);
68
+ };
69
+ return {
70
+ async start(w) {
71
+ if (chatId === undefined || key === undefined) {
72
+ throw new Error("morda: start() requires a chat");
73
+ }
74
+ const old = await storage.get(key);
75
+ if (old)
76
+ for (const win of old.stack)
77
+ options.onLeave?.(chatId, win);
78
+ const view = await renderWindow(w, dctx);
79
+ const msg = await ctx.send(view.text, { reply_markup: renderKeyboard(w, view) });
80
+ await storage.set(key, { stack: [w], messageId: msg.message_id, chatId });
81
+ },
82
+ async push(w) {
83
+ const st = await load();
84
+ if (!st)
85
+ throw new Error("morda: push() with no active dialog");
86
+ st.stack.push(w);
87
+ await edit(st);
88
+ },
89
+ async replace(w) {
90
+ const st = await load();
91
+ if (!st)
92
+ throw new Error("morda: replace() with no active dialog");
93
+ const oldTop = st.stack[st.stack.length - 1];
94
+ if (oldTop !== undefined && chatId !== undefined)
95
+ options.onLeave?.(chatId, oldTop);
96
+ st.stack[st.stack.length - 1] = w;
97
+ await edit(st);
98
+ },
99
+ async back() {
100
+ const st = await load();
101
+ if (!st || key === undefined)
102
+ return;
103
+ const popped = st.stack.pop();
104
+ if (popped !== undefined && chatId !== undefined)
105
+ options.onLeave?.(chatId, popped);
106
+ if (st.stack.length === 0) {
107
+ await ctx.api.call("deleteMessage", {
108
+ chat_id: st.chatId,
109
+ message_id: st.messageId,
110
+ });
111
+ await storage.delete(key);
112
+ return;
113
+ }
114
+ await edit(st);
115
+ },
116
+ async rerender() {
117
+ const st = await load();
118
+ if (st)
119
+ await edit(st);
120
+ },
121
+ };
122
+ };
123
+ // the Out type flows from `derive`, so no unsafe plugin cast is needed
124
+ const plugin = (composer) => composer
125
+ // expose ctx.dialog on every update
126
+ .derive((ctx) => ({ dialog: control(ctx) }))
127
+ // route dialog button presses; pass through everything else
128
+ .use(async (ctx, next) => {
129
+ const data = ctx.callbackQuery?.data;
130
+ if (data === undefined || !cd.filter(data))
131
+ return next();
132
+ const payload = cd.unpack(data);
133
+ if (!payload)
134
+ return next();
135
+ // clear the loading spinner; harmless if onClick answers again below
136
+ const answer = () => ctx.answerCallbackQuery().catch(() => { });
137
+ // stale-press guard: ignore presses whose window isn't the live stack top
138
+ // (e.g. a double-tap on an old keyboard before the edit landed).
139
+ const key = ctx.chat?.id?.toString();
140
+ const state = key === undefined ? undefined : await storage.get(key);
141
+ if (!state || state.stack[state.stack.length - 1] !== payload.w) {
142
+ await answer();
143
+ return;
144
+ }
145
+ const window = def[payload.w];
146
+ if (window) {
147
+ const view = await window(ctx);
148
+ const btn = findButton(view, payload.b);
149
+ if (btn?.onClick)
150
+ await btn.onClick(ctx);
151
+ }
152
+ await answer();
153
+ });
154
+ return plugin;
155
+ }
156
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,136 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { Composer, Context } from "@yaebal/core";
4
+ import { MemoryStorage } from "@yaebal/session";
5
+ import { back, button, dialogs, switchTo } from "./index.js";
6
+ const noop = async () => { };
7
+ const entry = (c) => c.toMiddleware();
8
+ function fakeApi() {
9
+ const calls = [];
10
+ let nextId = 100;
11
+ const api = {
12
+ sendMessage(params) {
13
+ calls.push({ method: "sendMessage", params });
14
+ return Promise.resolve({ message_id: nextId++ });
15
+ },
16
+ answerCallbackQuery(params) {
17
+ calls.push({ method: "answerCallbackQuery", params });
18
+ return Promise.resolve(true);
19
+ },
20
+ call(method, params) {
21
+ calls.push({ method, params });
22
+ return Promise.resolve(true);
23
+ },
24
+ };
25
+ return { api, calls };
26
+ }
27
+ const msgCtx = (api, text, chatId) => new Context({
28
+ api,
29
+ update: {
30
+ update_id: 1,
31
+ message: {
32
+ message_id: 1,
33
+ date: 0,
34
+ chat: { id: chatId, type: "private" },
35
+ from: { id: chatId, is_bot: false, first_name: "u" },
36
+ text,
37
+ },
38
+ },
39
+ updateType: "message",
40
+ });
41
+ const cbCtx = (api, data, chatId, messageId) => new Context({
42
+ api,
43
+ update: {
44
+ update_id: 1,
45
+ callback_query: {
46
+ id: "cb",
47
+ from: { id: chatId, is_bot: false, first_name: "u" },
48
+ message: { message_id: messageId, date: 0, chat: { id: chatId, type: "private" } },
49
+ data,
50
+ },
51
+ },
52
+ updateType: "callback_query",
53
+ });
54
+ const def = {
55
+ main: () => ({
56
+ text: "main",
57
+ keyboard: [
58
+ [switchTo("settings →", "settings")],
59
+ [button("ping", { id: "ping", onClick: (c) => c.answerCallbackQuery({ text: "pong" }) })],
60
+ ],
61
+ }),
62
+ settings: () => ({ text: "Settings", keyboard: [[back("← Back")]] }),
63
+ };
64
+ // read a button's callback_data straight from a recorded keyboard — no coupling
65
+ // to morda's internal encoding.
66
+ // biome-ignore lint/suspicious/noExplicitAny: reaching into recorded params
67
+ const dataAt = (params, row, col) => params.reply_markup.inline_keyboard[row][col].callback_data;
68
+ test("start → push(settings) → back navigates and edits in place", async () => {
69
+ const { api, calls } = fakeApi();
70
+ const storage = new MemoryStorage();
71
+ const mw = entry(new Composer()
72
+ .install(dialogs(def, { storage }))
73
+ .command("go", (ctx) => ctx.dialog.start("main")));
74
+ await mw(msgCtx(api, "/go", 1), noop);
75
+ const sent = calls.find((c) => c.method === "sendMessage");
76
+ assert.equal(sent?.params.text, "main");
77
+ const state = await storage.get("1");
78
+ assert.deepEqual(state?.stack, ["main"]);
79
+ // press "settings →"
80
+ calls.length = 0;
81
+ await mw(cbCtx(api, dataAt(sent?.params, 0, 0), 1, state?.messageId ?? 0), noop);
82
+ const edit1 = calls.find((c) => c.method === "editMessageText");
83
+ assert.equal(edit1?.params.text, "Settings");
84
+ assert.deepEqual((await storage.get("1"))?.stack, ["main", "settings"]);
85
+ // press "← back"
86
+ calls.length = 0;
87
+ await mw(cbCtx(api, dataAt(edit1?.params, 0, 0), 1, state?.messageId ?? 0), noop);
88
+ const edit2 = calls.find((c) => c.method === "editMessageText");
89
+ assert.equal(edit2?.params.text, "main");
90
+ assert.deepEqual((await storage.get("1"))?.stack, ["main"]);
91
+ });
92
+ test("onClick runs without navigating", async () => {
93
+ const { api, calls } = fakeApi();
94
+ const storage = new MemoryStorage();
95
+ const mw = entry(new Composer()
96
+ .install(dialogs(def, { storage }))
97
+ .command("go", (ctx) => ctx.dialog.start("main")));
98
+ await mw(msgCtx(api, "/go", 1), noop);
99
+ const sent = calls.find((c) => c.method === "sendMessage");
100
+ calls.length = 0;
101
+ // press "ping" (row 1)
102
+ await mw(cbCtx(api, dataAt(sent?.params, 1, 0), 1, 100), noop);
103
+ assert.ok(calls.some((c) => c.method === "answerCallbackQuery" && c.params.text === "pong"));
104
+ assert.equal(calls.find((c) => c.method === "editMessageText"), undefined);
105
+ assert.deepEqual((await storage.get("1"))?.stack, ["main"]); // unchanged
106
+ });
107
+ test("a press whose window is not the stack top is ignored", async () => {
108
+ const { api, calls } = fakeApi();
109
+ const storage = new MemoryStorage();
110
+ const mw = entry(new Composer()
111
+ .install(dialogs(def, { storage }))
112
+ .command("go", (ctx) => ctx.dialog.start("main")));
113
+ await mw(msgCtx(api, "/go", 1), noop);
114
+ const sent = calls.find((c) => c.method === "sendMessage");
115
+ // navigate to settings → stack top is now "settings"
116
+ await mw(cbCtx(api, dataAt(sent?.params, 0, 0), 1, 100), noop);
117
+ calls.length = 0;
118
+ // press the OLD "ping" button from main (payload.w = "main") while on "settings"
119
+ await mw(cbCtx(api, dataAt(sent?.params, 1, 0), 1, 100), noop);
120
+ assert.equal(calls.some((c) => c.method === "answerCallbackQuery" && c.params.text === "pong"), false);
121
+ assert.deepEqual((await storage.get("1"))?.stack, ["main", "settings"]); // unchanged
122
+ });
123
+ test("back() at the root closes the dialog", async () => {
124
+ const { api, calls } = fakeApi();
125
+ const storage = new MemoryStorage();
126
+ const mw = entry(new Composer()
127
+ .install(dialogs(def, { storage }))
128
+ .command("go", (ctx) => ctx.dialog.start("main"))
129
+ .command("close", (ctx) => ctx.dialog.back()));
130
+ await mw(msgCtx(api, "/go", 2), noop);
131
+ calls.length = 0;
132
+ await mw(msgCtx(api, "/close", 2), noop);
133
+ assert.ok(calls.some((c) => c.method === "deleteMessage"));
134
+ assert.equal(await storage.get("2"), undefined);
135
+ });
136
+ //# sourceMappingURL=index.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import type { Context, Plugin, User } from "@yaebal/core";
2
+ import { type DialogControl, type DialogsOptions } from "../index.js";
3
+ import { type VNode } from "./jsx-runtime.js";
4
+ export type { VNode } from "./jsx-runtime.js";
5
+ /** a screen is a component: a zero-arg function returning a `<Screen>` tree. */
6
+ export type ScreenComponent = () => VNode;
7
+ export interface ButtonProps {
8
+ id: string;
9
+ onClick?: () => unknown;
10
+ children?: unknown;
11
+ }
12
+ export declare function Screen(props: {
13
+ children?: unknown;
14
+ }): VNode;
15
+ export declare function ButtonRow(props: {
16
+ children?: unknown;
17
+ }): VNode;
18
+ export declare function Button(props: ButtonProps): VNode;
19
+ /** per-frame state. `setState` re-renders the screen in place and resolves when the edit lands. */
20
+ export declare function useState<T>(initial: T | (() => T)): [T, (next: T | ((prev: T) => T)) => Promise<void>];
21
+ /**
22
+ * run an effect after the screen renders. `deps` gate re-runs.
23
+ * prefer passing `deps` — the no-deps form fires on every *internal* render
24
+ * (a button press renders once to route and again to commit), not once per tap.
25
+ */
26
+ export declare function useEffect(fn: () => unknown, deps?: unknown[]): void;
27
+ export interface Navigation {
28
+ push(target: ScreenComponent | string): Promise<void>;
29
+ replace(target: ScreenComponent | string): Promise<void>;
30
+ back(): Promise<void>;
31
+ }
32
+ /** navigate by screen component (or window id). */
33
+ export declare function useNavigation(): Navigation;
34
+ /** the telegram user behind the current update. */
35
+ export declare function useUser(): User | undefined;
36
+ /** the session session (requires `@yaebal/session` installed before the dialog). */
37
+ export declare function useSession<S>(): S;
38
+ export interface Translation {
39
+ t(key: string, ...args: unknown[]): string;
40
+ changeLanguage(language: string): unknown;
41
+ }
42
+ /** i18n helpers (requires `@yaebal/i18n` installed before the dialog). */
43
+ export declare function useTranslation(): Translation;
44
+ /** install a set of JSX screens as a dialog. `bot.install(jsxDialogs({ main, settings }))`. */
45
+ export declare function jsxDialogs(screens: Record<string, ScreenComponent>, options?: DialogsOptions): Plugin<Context, {
46
+ dialog: DialogControl;
47
+ }>;
48
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/jsx/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAEN,KAAK,aAAa,EAElB,KAAK,cAAc,EAInB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAmB,KAAK,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAE/D,YAAY,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAE9C,gFAAgF;AAChF,MAAM,MAAM,eAAe,GAAG,MAAM,KAAK,CAAC;AAE1C,MAAM,WAAW,WAAW;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AAGD,wBAAgB,MAAM,CAAC,KAAK,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,KAAK,CAE3D;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,KAAK,CAE9D;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,KAAK,CAEhD;AAmBD,mGAAmG;AACnG,wBAAgB,QAAQ,CAAC,CAAC,EACzB,OAAO,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,GACpB,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,CAkBpD;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,OAAO,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,GAAG,IAAI,CASnE;AAOD,MAAM,WAAW,UAAU;IAC1B,IAAI,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,OAAO,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACtB;AAED,mDAAmD;AACnD,wBAAgB,aAAa,IAAI,UAAU,CAkB1C;AAED,mDAAmD;AACnD,wBAAgB,OAAO,IAAI,IAAI,GAAG,SAAS,CAE1C;AAED,oFAAoF;AACpF,wBAAgB,UAAU,CAAC,CAAC,KAAK,CAAC,CAEjC;AAED,MAAM,WAAW,WAAW;IAC3B,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;IAC3C,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1C;AAED,0EAA0E;AAC1E,wBAAgB,cAAc,IAAI,WAAW,CAQ5C;AA0JD,+FAA+F;AAC/F,wBAAgB,UAAU,CACzB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,EACxC,OAAO,CAAC,EAAE,cAAc,GACtB,MAAM,CAAC,OAAO,EAAE;IAAE,MAAM,EAAE,aAAa,CAAA;CAAE,CAAC,CAkB5C"}