@will-17173/telegram-cli 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.
Files changed (82) hide show
  1. package/LICENSE +675 -0
  2. package/README.md +264 -0
  3. package/README.zh-CN.md +264 -0
  4. package/dist/account/account-context.js +22 -0
  5. package/dist/account/account-context.js.map +1 -0
  6. package/dist/account/account-presets.js +22 -0
  7. package/dist/account/account-presets.js.map +1 -0
  8. package/dist/account/account-store.js +225 -0
  9. package/dist/account/account-store.js.map +1 -0
  10. package/dist/cli/app.js +21 -0
  11. package/dist/cli/app.js.map +1 -0
  12. package/dist/cli/output.js +39 -0
  13. package/dist/cli/output.js.map +1 -0
  14. package/dist/commands/account-options.js +55 -0
  15. package/dist/commands/account-options.js.map +1 -0
  16. package/dist/commands/account.js +449 -0
  17. package/dist/commands/account.js.map +1 -0
  18. package/dist/commands/config.js +63 -0
  19. package/dist/commands/config.js.map +1 -0
  20. package/dist/commands/data.js +52 -0
  21. package/dist/commands/data.js.map +1 -0
  22. package/dist/commands/query.js +116 -0
  23. package/dist/commands/query.js.map +1 -0
  24. package/dist/commands/telegram.js +423 -0
  25. package/dist/commands/telegram.js.map +1 -0
  26. package/dist/commands/types.js +12 -0
  27. package/dist/commands/types.js.map +1 -0
  28. package/dist/config/credential-store.js +82 -0
  29. package/dist/config/credential-store.js.map +1 -0
  30. package/dist/config/env.js +78 -0
  31. package/dist/config/env.js.map +1 -0
  32. package/dist/index.js +4 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/presenters/human.js +218 -0
  35. package/dist/presenters/human.js.map +1 -0
  36. package/dist/presenters/ink/DetailView.js +14 -0
  37. package/dist/presenters/ink/DetailView.js.map +1 -0
  38. package/dist/presenters/ink/TableView.js +9 -0
  39. package/dist/presenters/ink/TableView.js.map +1 -0
  40. package/dist/presenters/ink/TimelineView.js +34 -0
  41. package/dist/presenters/ink/TimelineView.js.map +1 -0
  42. package/dist/presenters/ink/display-width.js +149 -0
  43. package/dist/presenters/ink/display-width.js.map +1 -0
  44. package/dist/presenters/ink/image-preview.js +47 -0
  45. package/dist/presenters/ink/image-preview.js.map +1 -0
  46. package/dist/presenters/ink/listen-scroll.js +37 -0
  47. package/dist/presenters/ink/listen-scroll.js.map +1 -0
  48. package/dist/presenters/ink/listen-scrollbar.js +49 -0
  49. package/dist/presenters/ink/listen-scrollbar.js.map +1 -0
  50. package/dist/presenters/ink/listen.js +542 -0
  51. package/dist/presenters/ink/listen.js.map +1 -0
  52. package/dist/presenters/ink/mouse-scroll.js +55 -0
  53. package/dist/presenters/ink/mouse-scroll.js.map +1 -0
  54. package/dist/presenters/ink/render.js +45 -0
  55. package/dist/presenters/ink/render.js.map +1 -0
  56. package/dist/presenters/listen-message.js +267 -0
  57. package/dist/presenters/listen-message.js.map +1 -0
  58. package/dist/presenters/structured.js +30 -0
  59. package/dist/presenters/structured.js.map +1 -0
  60. package/dist/services/attachment-download.js +21 -0
  61. package/dist/services/attachment-download.js.map +1 -0
  62. package/dist/services/data-service.js +76 -0
  63. package/dist/services/data-service.js.map +1 -0
  64. package/dist/services/listen-album-aggregator.js +72 -0
  65. package/dist/services/listen-album-aggregator.js.map +1 -0
  66. package/dist/services/message-service.js +102 -0
  67. package/dist/services/message-service.js.map +1 -0
  68. package/dist/services/query-service.js +116 -0
  69. package/dist/services/query-service.js.map +1 -0
  70. package/dist/services/sync-service.js +134 -0
  71. package/dist/services/sync-service.js.map +1 -0
  72. package/dist/storage/chat-resolver.js +9 -0
  73. package/dist/storage/chat-resolver.js.map +1 -0
  74. package/dist/storage/message-db.js +246 -0
  75. package/dist/storage/message-db.js.map +1 -0
  76. package/dist/telegram/client-factory.js +27 -0
  77. package/dist/telegram/client-factory.js.map +1 -0
  78. package/dist/telegram/mtcute-client.js +324 -0
  79. package/dist/telegram/mtcute-client.js.map +1 -0
  80. package/dist/telegram/types.js +2 -0
  81. package/dist/telegram/types.js.map +1 -0
  82. package/package.json +50 -0
@@ -0,0 +1,82 @@
1
+ import { chmodSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync, } from 'node:fs';
2
+ import { randomUUID } from 'node:crypto';
3
+ import { dirname, join } from 'node:path';
4
+ const API_ID_PATTERN = /^[1-9]\d*$/;
5
+ const MISSING_CREDENTIALS_MESSAGE = 'Telegram API credentials are not configured. Run tg config set --api-id <id> --api-hash <hash>.';
6
+ const INVALID_CREDENTIALS_MESSAGE = 'Stored Telegram API configuration is invalid.';
7
+ export class MissingCredentialsError extends Error {
8
+ constructor() {
9
+ super(MISSING_CREDENTIALS_MESSAGE);
10
+ this.name = 'MissingCredentialsError';
11
+ }
12
+ }
13
+ export function validateCredentials(input) {
14
+ const rawId = String(input.apiId ?? '').trim();
15
+ const apiHash = input.apiHash?.trim() ?? '';
16
+ const apiId = Number.parseInt(rawId, 10);
17
+ if (!API_ID_PATTERN.test(rawId) || !Number.isSafeInteger(apiId)) {
18
+ throw new Error('API ID must be a positive integer.');
19
+ }
20
+ if (!apiHash) {
21
+ throw new Error('API hash is required.');
22
+ }
23
+ return { apiId, apiHash };
24
+ }
25
+ export function readCredentials(path) {
26
+ let contents;
27
+ try {
28
+ contents = readFileSync(path, 'utf8');
29
+ }
30
+ catch (error) {
31
+ if (isNodeError(error) && error.code === 'ENOENT') {
32
+ throw new MissingCredentialsError();
33
+ }
34
+ throw new Error(INVALID_CREDENTIALS_MESSAGE);
35
+ }
36
+ try {
37
+ const parsed = JSON.parse(contents);
38
+ if (!isCredentialDocument(parsed)) {
39
+ throw new Error(INVALID_CREDENTIALS_MESSAGE);
40
+ }
41
+ return validateCredentials({ apiId: parsed.api_id, apiHash: parsed.api_hash });
42
+ }
43
+ catch {
44
+ throw new Error(INVALID_CREDENTIALS_MESSAGE);
45
+ }
46
+ }
47
+ export function writeCredentials(path, credentials) {
48
+ const normalized = validateCredentials(credentials);
49
+ const parent = dirname(path);
50
+ const temporaryPath = join(parent, `.${randomUUID()}.tmp`);
51
+ const document = `${JSON.stringify({
52
+ api_id: normalized.apiId,
53
+ api_hash: normalized.apiHash,
54
+ }, null, 2)}\n`;
55
+ mkdirSync(parent, { recursive: true });
56
+ let renamed = false;
57
+ try {
58
+ writeFileSync(temporaryPath, document, { encoding: 'utf8', flag: 'wx', mode: 0o600 });
59
+ chmodSync(temporaryPath, 0o600);
60
+ renameSync(temporaryPath, path);
61
+ renamed = true;
62
+ chmodSync(path, 0o600);
63
+ }
64
+ finally {
65
+ if (!renamed) {
66
+ rmSync(temporaryPath, { force: true });
67
+ }
68
+ }
69
+ }
70
+ function isCredentialDocument(value) {
71
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
72
+ return false;
73
+ const document = value;
74
+ return (Number.isSafeInteger(document.api_id) &&
75
+ document.api_id > 0 &&
76
+ typeof document.api_hash === 'string' &&
77
+ document.api_hash.trim().length > 0);
78
+ }
79
+ function isNodeError(error) {
80
+ return error instanceof Error && 'code' in error;
81
+ }
82
+ //# sourceMappingURL=credential-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential-store.js","sourceRoot":"","sources":["../../src/config/credential-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,78 @@
1
+ import { mkdirSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { isAbsolute, join, resolve } from 'node:path';
4
+ import { MissingCredentialsError, readCredentials, validateCredentials, } from './credential-store.js';
5
+ const APP_NAME = 'tg-cli';
6
+ const DEFAULT_API_ID = 2040;
7
+ const DEFAULT_API_HASH = 'b18441a1ff607e10a989891a5462e627';
8
+ const PARTIAL_CREDENTIALS_MESSAGE = 'TG_API_ID and TG_API_HASH must be provided together.';
9
+ export function getTelegramCredentials() {
10
+ const apiId = process.env.TG_API_ID?.trim() ?? '';
11
+ const apiHash = process.env.TG_API_HASH?.trim() ?? '';
12
+ if (Boolean(apiId) !== Boolean(apiHash)) {
13
+ throw new Error(PARTIAL_CREDENTIALS_MESSAGE);
14
+ }
15
+ if (apiId && apiHash) {
16
+ return {
17
+ ...validateCredentials({ apiId, apiHash }),
18
+ source: 'environment',
19
+ };
20
+ }
21
+ try {
22
+ return {
23
+ ...readCredentials(getConfigPath()),
24
+ source: 'stored',
25
+ };
26
+ }
27
+ catch (error) {
28
+ if (!(error instanceof MissingCredentialsError))
29
+ throw error;
30
+ return {
31
+ apiId: DEFAULT_API_ID,
32
+ apiHash: DEFAULT_API_HASH,
33
+ source: 'default',
34
+ };
35
+ }
36
+ }
37
+ export function getSessionName() {
38
+ const raw = process.env.TG_SESSION_NAME;
39
+ return raw && raw.trim() ? raw.trim() : 'tg_cli';
40
+ }
41
+ export function getDataDir() {
42
+ const raw = process.env.DATA_DIR;
43
+ const dir = raw && raw.trim() ? resolvePath(raw) : join(defaultDataHome(), APP_NAME);
44
+ mkdirSync(dir, { recursive: true });
45
+ return dir;
46
+ }
47
+ export function getConfigPath() {
48
+ return join(getDataDir(), 'config.json');
49
+ }
50
+ export function getAccountRegistryPath(dataDir) {
51
+ const root = dataDir && dataDir.trim() ? resolvePath(dataDir) : getDataDir();
52
+ return join(root, 'accounts.json');
53
+ }
54
+ export function getDbPath() {
55
+ const raw = process.env.DB_PATH;
56
+ const path = raw && raw.trim() ? resolvePath(raw) : join(getDataDir(), 'messages.db');
57
+ mkdirSync(resolve(path, '..'), { recursive: true });
58
+ return path;
59
+ }
60
+ export function getSessionPath() {
61
+ const dir = join(getDataDir(), 'sessions');
62
+ mkdirSync(dir, { recursive: true });
63
+ return join(dir, getSessionName());
64
+ }
65
+ function resolvePath(raw) {
66
+ const expanded = raw.startsWith('~/') ? join(homedir(), raw.slice(2)) : raw;
67
+ return isAbsolute(expanded) ? expanded : resolve(process.cwd(), expanded);
68
+ }
69
+ function defaultDataHome() {
70
+ if (process.env.XDG_DATA_HOME)
71
+ return process.env.XDG_DATA_HOME;
72
+ if (process.platform === 'darwin')
73
+ return join(homedir(), 'Library', 'Application Support');
74
+ if (process.platform === 'win32')
75
+ return process.env.LOCALAPPDATA || join(homedir(), 'AppData', 'Local');
76
+ return join(homedir(), '.local', 'share');
77
+ }
78
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/config/env.ts"],"names":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { createApp } from './cli/app.js';
3
+ await createApp().parseAsync(process.argv);
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAExC,MAAM,SAAS,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA"}
@@ -0,0 +1,218 @@
1
+ const MAX_NESTING_DEPTH = 4;
2
+ const MAX_COLLECTION_ENTRIES = 20;
3
+ const MAX_STRING_LENGTH = 200;
4
+ const MAX_RENDERED_LENGTH = 2_000;
5
+ export function chatTable(chats) {
6
+ return {
7
+ kind: 'table',
8
+ title: 'Chats',
9
+ columns: ['ID', 'NAME', 'TYPE', 'UNREAD'],
10
+ rows: chats.map((chat) => [String(chat.id), display(chat.name), display(chat.type), String(chat.unread)]),
11
+ emptyText: 'No chats found.',
12
+ };
13
+ }
14
+ export function userDetail(user, title = 'User') {
15
+ return {
16
+ kind: 'detail',
17
+ title,
18
+ fields: [
19
+ { label: 'Name', value: display(user.name) },
20
+ { label: 'Username', value: username(user.username) },
21
+ { label: 'ID', value: display(user.id) },
22
+ { label: 'Phone', value: display(user.phone) },
23
+ ],
24
+ };
25
+ }
26
+ export function recordDetail(title, record) {
27
+ return {
28
+ kind: 'detail',
29
+ title,
30
+ fields: Object.entries(record).map(([label, value]) => ({ label, value: display(value) })),
31
+ };
32
+ }
33
+ export function actionDetail(title, values) {
34
+ return {
35
+ kind: 'detail',
36
+ title,
37
+ fields: Object.entries(values).map(([label, value]) => {
38
+ const field = { label, value: display(value) };
39
+ if (value === true)
40
+ field.tone = 'success';
41
+ if (value === false)
42
+ field.tone = 'danger';
43
+ return field;
44
+ }),
45
+ };
46
+ }
47
+ export function messageTable(messages, title = 'Messages', emptyText = 'No messages found.') {
48
+ return {
49
+ kind: 'table',
50
+ title,
51
+ columns: ['TIME', 'CHAT', 'SENDER', 'MESSAGE'],
52
+ rows: messages.map((message) => [
53
+ display(message.timestamp),
54
+ display(message.chat_name),
55
+ display(message.sender_name),
56
+ display(message.content),
57
+ ]),
58
+ emptyText,
59
+ };
60
+ }
61
+ export function statsSummary(stats, title = 'Stats', chats) {
62
+ const summary = {
63
+ kind: 'summary',
64
+ title,
65
+ fields: Object.entries(stats).map(([label, value]) => ({
66
+ label: sentenceCase(label),
67
+ value: String(value),
68
+ })),
69
+ };
70
+ if (chats != null) {
71
+ summary.table = {
72
+ columns: ['CHAT', 'MESSAGES'],
73
+ rows: chats.map((chat) => [display(chat.chat_name), String(chat.msg_count)]),
74
+ emptyText: 'No chats found.',
75
+ };
76
+ }
77
+ return summary;
78
+ }
79
+ export function topTable(title, rows) {
80
+ return {
81
+ kind: 'table',
82
+ title,
83
+ columns: ['NAME', 'COUNT'],
84
+ rows: rows.map((row) => [
85
+ display(row.name ?? row.sender_name ?? row.chat_name),
86
+ display(row.count ?? row.msg_count),
87
+ ]),
88
+ emptyText: 'No results found.',
89
+ };
90
+ }
91
+ export function timelineView(title, rows) {
92
+ return {
93
+ kind: 'timeline',
94
+ title,
95
+ rows: rows.map((row) => ({ period: row.period, count: row.count ?? row.msg_count ?? 0 })),
96
+ };
97
+ }
98
+ export function syncSummary(result) {
99
+ if (result.results == null) {
100
+ const count = result.synced ?? result.stored ?? 0;
101
+ return {
102
+ kind: 'summary',
103
+ title: 'Sync complete',
104
+ fields: [
105
+ { label: 'Chat', value: display(result.chat) },
106
+ { label: 'Messages', value: String(count), tone: 'success' },
107
+ ],
108
+ };
109
+ }
110
+ const failures = result.failures ?? {};
111
+ const failureCount = Object.keys(failures).length;
112
+ const selectedChats = result.chats ?? Object.keys(result.results).length;
113
+ const allFailed = selectedChats > 0 && failureCount >= selectedChats;
114
+ const title = allFailed ? 'Sync failed' : failureCount > 0 ? 'Sync partially complete' : 'Sync complete';
115
+ const newMessagesTone = allFailed ? 'danger' : failureCount > 0 ? 'warning' : 'success';
116
+ return {
117
+ kind: 'summary',
118
+ title,
119
+ fields: [
120
+ { label: 'Chats', value: String(selectedChats) },
121
+ { label: 'New messages', value: String(result.new_messages ?? sum(Object.values(result.results))), tone: newMessagesTone },
122
+ { label: 'Failures', value: String(failureCount), tone: failureCount ? 'danger' : 'success' },
123
+ ],
124
+ table: {
125
+ columns: ['CHAT', 'MESSAGES', 'STATUS'],
126
+ rows: Object.entries(result.results).map(([chat, count]) => [chat, String(count), failures[chat] ?? 'OK']),
127
+ emptyText: 'No chats synced.',
128
+ },
129
+ };
130
+ }
131
+ function display(value) {
132
+ if (value == null || value === '')
133
+ return '—';
134
+ if (typeof value === 'string')
135
+ return truncate(formatTimestamp(value), MAX_STRING_LENGTH, '…');
136
+ if (typeof value === 'object') {
137
+ return safeJson(value);
138
+ }
139
+ return String(value);
140
+ }
141
+ function safeJson(value) {
142
+ const seen = new WeakSet();
143
+ try {
144
+ const bounded = boundValue(value, 0, seen);
145
+ return truncate(JSON.stringify(bounded) ?? '—', MAX_RENDERED_LENGTH, '… [truncated]');
146
+ }
147
+ catch (error) {
148
+ return `[Unserializable: ${error instanceof Error ? error.message : String(error)}]`;
149
+ }
150
+ }
151
+ function boundValue(value, depth, seen) {
152
+ if (typeof value === 'bigint')
153
+ return String(value);
154
+ if (typeof value === 'string')
155
+ return truncate(value, MAX_STRING_LENGTH, '…');
156
+ if (value == null || typeof value !== 'object')
157
+ return value;
158
+ if (seen.has(value))
159
+ return '[Circular]';
160
+ if (depth >= MAX_NESTING_DEPTH)
161
+ return '[Max depth]';
162
+ seen.add(value);
163
+ if (Array.isArray(value)) {
164
+ const bounded = value.slice(0, MAX_COLLECTION_ENTRIES)
165
+ .map((item) => boundValue(item, depth + 1, seen));
166
+ if (value.length > MAX_COLLECTION_ENTRIES)
167
+ bounded.push(`… (+${value.length - MAX_COLLECTION_ENTRIES} more)`);
168
+ return bounded;
169
+ }
170
+ const entries = Object.entries(value);
171
+ const bounded = Object.fromEntries(entries.slice(0, MAX_COLLECTION_ENTRIES)
172
+ .map(([key, item]) => [key, boundValue(item, depth + 1, seen)]));
173
+ if (entries.length > MAX_COLLECTION_ENTRIES)
174
+ bounded[`… (+${entries.length - MAX_COLLECTION_ENTRIES} more)`] = null;
175
+ return bounded;
176
+ }
177
+ function truncate(value, maximum, marker) {
178
+ if (value.length <= maximum)
179
+ return value;
180
+ return value.slice(0, Math.max(0, maximum - marker.length)) + marker;
181
+ }
182
+ function username(value) {
183
+ const text = display(value);
184
+ if (text === '—')
185
+ return text;
186
+ return text.startsWith('@') ? text : `@${text}`;
187
+ }
188
+ function formatTimestamp(value) {
189
+ const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2})(?:\.\d+)?)?(?:Z|[+-](\d{2}):(\d{2}))$/.exec(value);
190
+ if (match == null || !hasValidIsoComponents(match))
191
+ return value;
192
+ const date = new Date(value);
193
+ if (Number.isNaN(date.getTime()))
194
+ return value;
195
+ return `${pad(date.getFullYear(), 4)}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`;
196
+ }
197
+ function hasValidIsoComponents(match) {
198
+ const [year, month, day, hour, minute, second, offsetHour, offsetMinute] = match.slice(1).map((part) => Number(part ?? 0));
199
+ if (hour > 23 || minute > 59 || second > 59 || offsetHour > 23 || offsetMinute > 59)
200
+ return false;
201
+ const wallClock = new Date(0);
202
+ wallClock.setUTCFullYear(year, month - 1, day);
203
+ wallClock.setUTCHours(hour, minute, second, 0);
204
+ return wallClock.getUTCFullYear() === year
205
+ && wallClock.getUTCMonth() === month - 1
206
+ && wallClock.getUTCDate() === day;
207
+ }
208
+ function pad(value, length = 2) {
209
+ return String(value).padStart(length, '0');
210
+ }
211
+ function sentenceCase(value) {
212
+ const words = value.replaceAll('_', ' ');
213
+ return words.length === 0 ? words : words[0].toUpperCase() + words.slice(1);
214
+ }
215
+ function sum(values) {
216
+ return values.reduce((total, value) => total + value, 0);
217
+ }
218
+ //# sourceMappingURL=human.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"human.js","sourceRoot":"","sources":["../../src/presenters/human.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from 'ink';
3
+ import stringWidth from 'string-width';
4
+ const toneColors = {
5
+ default: undefined,
6
+ success: 'green',
7
+ warning: 'yellow',
8
+ danger: 'red',
9
+ };
10
+ export function DetailView({ title, fields }) {
11
+ const labelWidth = fields.reduce((width, field) => Math.max(width, stringWidth(field.label)), 0);
12
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", paddingX: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: title }), fields.map((field, index) => (_jsxs(Text, { children: [_jsxs(Text, { bold: true, children: [field.label, ' '.repeat(labelWidth - stringWidth(field.label))] }), _jsx(Text, { dimColor: true, children: ": " }), _jsx(Text, { color: toneColors[field.tone ?? 'default'], children: field.value })] }, index)))] }));
13
+ }
14
+ //# sourceMappingURL=DetailView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DetailView.js","sourceRoot":"","sources":["../../../src/presenters/ink/DetailView.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAC/B,OAAO,WAAW,MAAM,cAAc,CAAA;AAQtC,MAAM,UAAU,GAAG;IACjB,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,OAAO;IAChB,OAAO,EAAE,QAAQ;IACjB,MAAM,EAAE,KAAK;CACL,CAAA;AAEV,MAAM,UAAU,UAAU,CAAC,EAAE,KAAK,EAAE,MAAM,EAAmB;IAC3D,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IAEhG,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,WAAW,EAAC,OAAO,EAAC,QAAQ,EAAE,CAAC,aACzD,KAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAC,MAAM,YAAE,KAAK,GAAQ,EACrC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAC5B,MAAC,IAAI,eACH,MAAC,IAAI,IAAC,IAAI,mBAAE,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,UAAU,GAAG,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAQ,EAClF,KAAC,IAAI,IAAC,QAAQ,yBAAU,EACxB,KAAC,IAAI,IAAC,KAAK,EAAE,UAAU,CAAC,KAAK,CAAC,IAAI,IAAI,SAAS,CAAC,YAAG,KAAK,CAAC,KAAK,GAAQ,KAH7D,KAAK,CAIT,CACR,CAAC,IACE,CACP,CAAA;AACH,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from 'ink';
3
+ import { formatGridTable, truncateCell } from './display-width.js';
4
+ export function TableView({ title, columns, rows, emptyText = 'No results found.', terminalWidth, }) {
5
+ const width = terminalWidth ?? process.stdout.columns ?? 80;
6
+ const lines = formatGridTable(columns, rows, width, emptyText);
7
+ return (_jsxs(Box, { flexDirection: "column", children: [title ? _jsx(Text, { bold: true, color: "cyan", children: truncateCell(title, width) }) : null, lines.map((line, index) => (_jsx(Text, { bold: line.kind === 'header', dimColor: line.kind === 'border' || line.kind === 'separator', children: line.text }, `${line.kind}:${index}`)))] }));
8
+ }
9
+ //# sourceMappingURL=TableView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TableView.js","sourceRoot":"","sources":["../../../src/presenters/ink/TableView.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAC/B,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAUlE,MAAM,UAAU,SAAS,CAAC,EACxB,KAAK,EACL,OAAO,EACP,IAAI,EACJ,SAAS,GAAG,mBAAmB,EAC/B,aAAa,GACE;IACf,MAAM,KAAK,GAAG,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAA;IAC3D,MAAM,KAAK,GAAG,eAAe,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,CAAC,CAAA;IAE9D,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACxB,KAAK,CAAC,CAAC,CAAC,KAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAC,MAAM,YAAE,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,GAAQ,CAAC,CAAC,CAAC,IAAI,EAC1E,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAC1B,KAAC,IAAI,IAEH,IAAI,EAAE,IAAI,CAAC,IAAI,KAAK,QAAQ,EAC5B,QAAQ,EAAE,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW,YAE5D,IAAI,CAAC,IAAI,IAJL,GAAG,IAAI,CAAC,IAAI,IAAI,KAAK,EAAE,CAKvB,CACR,CAAC,IACE,CACP,CAAA;AACH,CAAC"}
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from 'ink';
3
+ import stringWidth from 'string-width';
4
+ import { truncateCell } from './display-width.js';
5
+ export function TimelineView({ title, rows, terminalWidth }) {
6
+ const width = normalizeWidth(terminalWidth ?? process.stdout.columns ?? 80);
7
+ const maximum = rows.reduce((value, row) => Math.max(value, row.count), 0);
8
+ if (width === 0)
9
+ return _jsx(Text, { children: '' });
10
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, color: "cyan", children: truncateCell(title, width) }), rows.length === 0
11
+ ? _jsx(Text, { dimColor: true, children: truncateCell('No activity found.', width) })
12
+ : rows.map((row, index) => _jsx(Text, { children: formatTimelineRow(row, maximum, width) }, index))] }));
13
+ }
14
+ function formatTimelineRow(row, maximum, width) {
15
+ const count = String(row.count);
16
+ if (width < 8)
17
+ return truncateCell(`${row.period} ${count}`, width);
18
+ const countWidth = Math.min(stringWidth(count), Math.max(1, Math.floor(width / 3)));
19
+ const countText = truncateCell(count, countWidth);
20
+ const remainingWidth = width - stringWidth(countText) - 2;
21
+ const periodWidth = Math.min(stringWidth(row.period), Math.max(1, Math.floor(remainingWidth / 3)));
22
+ const period = truncateCell(row.period, periodWidth);
23
+ const barWidth = Math.max(0, width - periodWidth - stringWidth(countText) - 2);
24
+ const barLength = maximum > 0 && row.count > 0
25
+ ? Math.max(1, Math.round(row.count / maximum * barWidth))
26
+ : 0;
27
+ const bar = '█'.repeat(Math.min(barLength, barWidth));
28
+ const line = `${period}${' '.repeat(periodWidth - stringWidth(period))} ${bar}${bar ? ' ' : ''}${countText}`;
29
+ return truncateCell(line, width);
30
+ }
31
+ function normalizeWidth(width) {
32
+ return Number.isFinite(width) ? Math.max(0, Math.floor(width)) : 0;
33
+ }
34
+ //# sourceMappingURL=TimelineView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TimelineView.js","sourceRoot":"","sources":["../../../src/presenters/ink/TimelineView.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,149 @@
1
+ import stringWidth from 'string-width';
2
+ const COLUMN_GAP = ' ';
3
+ const PRACTICAL_MINIMUM_WIDTH = 3;
4
+ const graphemeSegmenter = new Intl.Segmenter(undefined, { granularity: 'grapheme' });
5
+ export function truncateCell(value, maxWidth) {
6
+ const text = String(value);
7
+ const width = normalizeWidth(maxWidth);
8
+ if (width === 0)
9
+ return '';
10
+ if (stringWidth(text) <= width)
11
+ return text;
12
+ const contentWidth = width - stringWidth('…');
13
+ let result = '';
14
+ for (const { segment } of graphemeSegmenter.segment(text)) {
15
+ if (stringWidth(result + segment) > contentWidth)
16
+ break;
17
+ result += segment;
18
+ }
19
+ return `${result}…`;
20
+ }
21
+ export function formatTable(columns, rows, terminalWidth) {
22
+ if (columns.length === 0)
23
+ return [];
24
+ const widthLimit = normalizeWidth(terminalWidth);
25
+ const gapWidth = stringWidth(COLUMN_GAP) * (columns.length - 1);
26
+ const availableWidth = Math.max(0, widthLimit - gapWidth);
27
+ const naturalWidths = measureNaturalWidths(columns, rows);
28
+ const widths = allocateWidths(naturalWidths, availableWidth);
29
+ return [columns, ...rows].map((row) => {
30
+ const unboundedText = widths
31
+ .map((width, index) => padCell(truncateCell(row[index] ?? '', width), width))
32
+ .join(COLUMN_GAP);
33
+ const text = truncateCell(unboundedText, widthLimit);
34
+ return { text, width: stringWidth(text) };
35
+ });
36
+ }
37
+ export function formatGridTable(columns, rows, terminalWidth, emptyText = 'No results') {
38
+ if (columns.length === 0)
39
+ return [];
40
+ const widthLimit = normalizeWidth(terminalWidth);
41
+ const minimumGridWidth = columns.length * 4 + 1;
42
+ if (widthLimit < minimumGridWidth) {
43
+ return formatNarrowGrid(columns, rows, widthLimit, emptyText);
44
+ }
45
+ const gridOverhead = columns.length * 3 + 1;
46
+ const availableWidth = widthLimit - gridOverhead;
47
+ const naturalWidths = measureNaturalWidths(columns, rows);
48
+ const widths = allocateWidths(naturalWidths, availableWidth);
49
+ const lines = [
50
+ gridBorder(widths, '╭', '┬', '╮', 'border'),
51
+ gridRow(columns, widths, 'header'),
52
+ gridBorder(widths, '├', '┼', '┤', 'separator'),
53
+ ];
54
+ if (rows.length === 0) {
55
+ const innerWidth = sum(widths) + columns.length * 3 - 1;
56
+ const contentWidth = Math.max(0, innerWidth - 2);
57
+ const content = padCell(truncateCell(emptyText, contentWidth), contentWidth);
58
+ lines.push(makeGridLine(`│ ${content} │`, 'empty'));
59
+ }
60
+ else {
61
+ rows.forEach((row, index) => {
62
+ if (index > 0)
63
+ lines.push(gridBorder(widths, '├', '┼', '┤', 'separator'));
64
+ lines.push(gridRow(row, widths, 'row'));
65
+ });
66
+ }
67
+ lines.push(gridBorder(widths, '╰', '┴', '╯', 'border'));
68
+ return lines;
69
+ }
70
+ function formatNarrowGrid(columns, rows, width, emptyText) {
71
+ if (width === 0)
72
+ return [];
73
+ if (width === 1)
74
+ return [makeGridLine('…', 'empty')];
75
+ if (width === 2)
76
+ return [makeGridLine('╭╮', 'border')];
77
+ const innerWidth = width - 2;
78
+ const source = rows.length === 0 ? emptyText : (rows[0]?.[0] ?? columns[0] ?? '');
79
+ const content = padCell(truncateCell(source, innerWidth), innerWidth);
80
+ return [
81
+ makeGridLine(`╭${'─'.repeat(innerWidth)}╮`, 'border'),
82
+ makeGridLine(`│${content}│`, rows.length === 0 ? 'empty' : 'row'),
83
+ makeGridLine(`╰${'─'.repeat(innerWidth)}╯`, 'border'),
84
+ ];
85
+ }
86
+ function gridBorder(widths, left, junction, right, kind) {
87
+ const text = left + widths.map((width) => '─'.repeat(width + 2)).join(junction) + right;
88
+ return makeGridLine(text, kind);
89
+ }
90
+ function gridRow(values, widths, kind) {
91
+ const cells = widths.map((width, index) => {
92
+ return ` ${padCell(truncateCell(values[index] ?? '', width), width)} `;
93
+ });
94
+ return makeGridLine(`│${cells.join('│')}│`, kind);
95
+ }
96
+ function makeGridLine(text, kind) {
97
+ return { text, width: stringWidth(text), kind };
98
+ }
99
+ function measureNaturalWidths(columns, rows) {
100
+ const widths = columns.map((column) => stringWidth(column));
101
+ for (const row of rows) {
102
+ const columnCount = Math.min(row.length, widths.length);
103
+ for (let index = 0; index < columnCount; index++) {
104
+ widths[index] = Math.max(widths[index], stringWidth(row[index] ?? ''));
105
+ }
106
+ }
107
+ return widths;
108
+ }
109
+ function allocateWidths(naturalWidths, availableWidth) {
110
+ if (sum(naturalWidths) <= availableWidth)
111
+ return naturalWidths;
112
+ const practicalMinimums = naturalWidths.map((width) => Math.min(width, PRACTICAL_MINIMUM_WIDTH));
113
+ const widths = sum(practicalMinimums) <= availableWidth
114
+ ? practicalMinimums
115
+ : naturalWidths.map(() => 0);
116
+ let remaining = availableWidth - sum(widths);
117
+ const capacities = naturalWidths
118
+ .map((width, index) => ({ index, capacity: width - widths[index] }))
119
+ .filter(({ capacity }) => capacity > 0)
120
+ .sort((left, right) => left.capacity - right.capacity);
121
+ while (remaining > 0 && capacities.length > 0) {
122
+ const share = Math.floor(remaining / capacities.length);
123
+ const smallest = capacities[0];
124
+ if (smallest.capacity <= share) {
125
+ widths[smallest.index] += smallest.capacity;
126
+ remaining -= smallest.capacity;
127
+ capacities.shift();
128
+ continue;
129
+ }
130
+ for (const { index } of capacities)
131
+ widths[index] += share;
132
+ remaining -= share * capacities.length;
133
+ for (let index = 0; index < remaining; index++) {
134
+ widths[capacities[index].index]++;
135
+ }
136
+ remaining = 0;
137
+ }
138
+ return widths;
139
+ }
140
+ function padCell(value, width) {
141
+ return value + ' '.repeat(Math.max(0, width - stringWidth(value)));
142
+ }
143
+ function normalizeWidth(width) {
144
+ return Number.isFinite(width) ? Math.max(0, Math.floor(width)) : 0;
145
+ }
146
+ function sum(values) {
147
+ return values.reduce((total, value) => total + value, 0);
148
+ }
149
+ //# sourceMappingURL=display-width.js.map