@takimcizgisi/ajan 0.2.5

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 (79) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +366 -0
  3. package/config/models.json +54 -0
  4. package/dist/cli/index.d.ts +2 -0
  5. package/dist/cli/index.js +741 -0
  6. package/dist/cli/tui.d.ts +145 -0
  7. package/dist/cli/tui.js +1003 -0
  8. package/dist/config/configManager.d.ts +18 -0
  9. package/dist/config/configManager.js +204 -0
  10. package/dist/config/types.d.ts +98 -0
  11. package/dist/config/types.js +2 -0
  12. package/dist/core/agent.d.ts +88 -0
  13. package/dist/core/agent.js +222 -0
  14. package/dist/electron/main.d.ts +1 -0
  15. package/dist/electron/main.js +364 -0
  16. package/dist/electron/preload.d.ts +1 -0
  17. package/dist/electron/preload.js +67 -0
  18. package/dist/engine/llama.d.ts +39 -0
  19. package/dist/engine/llama.js +146 -0
  20. package/dist/engine/modelManager.d.ts +18 -0
  21. package/dist/engine/modelManager.js +73 -0
  22. package/dist/index.d.ts +9 -0
  23. package/dist/index.js +9 -0
  24. package/dist/service.d.ts +90 -0
  25. package/dist/service.js +268 -0
  26. package/dist/tools/data.d.ts +3 -0
  27. package/dist/tools/data.js +111 -0
  28. package/dist/tools/edit.d.ts +2 -0
  29. package/dist/tools/edit.js +48 -0
  30. package/dist/tools/file.d.ts +6 -0
  31. package/dist/tools/file.js +274 -0
  32. package/dist/tools/filesys.d.ts +3 -0
  33. package/dist/tools/filesys.js +74 -0
  34. package/dist/tools/memory.d.ts +3 -0
  35. package/dist/tools/memory.js +69 -0
  36. package/dist/tools/patch.d.ts +15 -0
  37. package/dist/tools/patch.js +129 -0
  38. package/dist/tools/registry.d.ts +10 -0
  39. package/dist/tools/registry.js +64 -0
  40. package/dist/tools/taskComplete.d.ts +2 -0
  41. package/dist/tools/taskComplete.js +19 -0
  42. package/dist/tools/terminal.d.ts +2 -0
  43. package/dist/tools/terminal.js +130 -0
  44. package/dist/tools/utils.d.ts +7 -0
  45. package/dist/tools/utils.js +63 -0
  46. package/dist/tools/web.d.ts +8 -0
  47. package/dist/tools/web.js +181 -0
  48. package/dist/utils/clipboard.d.ts +2 -0
  49. package/dist/utils/clipboard.js +40 -0
  50. package/dist/utils/lock.d.ts +12 -0
  51. package/dist/utils/lock.js +68 -0
  52. package/dist/utils/paths.d.ts +13 -0
  53. package/dist/utils/paths.js +58 -0
  54. package/dist/utils/projectContext.d.ts +2 -0
  55. package/dist/utils/projectContext.js +24 -0
  56. package/dist/utils/sacGuard.d.ts +4 -0
  57. package/dist/utils/sacGuard.js +47 -0
  58. package/dist/utils/sessions.d.ts +29 -0
  59. package/dist/utils/sessions.js +64 -0
  60. package/dist/utils/spinner.d.ts +10 -0
  61. package/dist/utils/spinner.js +32 -0
  62. package/package.json +106 -0
  63. package/src/gui/assets/image/AJAN_LOGO.svg +1 -0
  64. package/src/gui/assets/image/ajan.png +0 -0
  65. package/src/gui/assets/image/logo.svg +1 -0
  66. package/src/gui/assets/image/opencode.svg +18 -0
  67. package/src/gui/css/style.css +430 -0
  68. package/src/gui/html/chat.html +34 -0
  69. package/src/gui/html/downloads.html +11 -0
  70. package/src/gui/html/hakkinda.html +26 -0
  71. package/src/gui/html/index.html +82 -0
  72. package/src/gui/html/projects.html +12 -0
  73. package/src/gui/html/settings.html +4 -0
  74. package/src/gui/js/app.js +197 -0
  75. package/src/gui/js/chat.js +434 -0
  76. package/src/gui/js/downloads.js +172 -0
  77. package/src/gui/js/hakkinda.js +16 -0
  78. package/src/gui/js/projects.js +113 -0
  79. package/src/gui/js/settings.js +177 -0
@@ -0,0 +1,1003 @@
1
+ import chalk from "chalk";
2
+ import { homedir } from "node:os";
3
+ const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
4
+ const BAR_FRAMES = [
5
+ "[> ]",
6
+ "[=> ]",
7
+ "[==> ]",
8
+ "[===> ]",
9
+ "[====> ]",
10
+ "[=====> ]",
11
+ "[======> ]",
12
+ "[=======>]",
13
+ "[========]"
14
+ ];
15
+ const ANSI_RE = /\x1b\[[0-9;]*[A-Za-z]/g;
16
+ const SIDEBAR_WIDTH = 22;
17
+ const SIDEBAR_MIN_COLS = 56;
18
+ const WHEEL_STEP = 3;
19
+ function cleanThinking(text) {
20
+ const cleaned = text
21
+ .replace(/^\s*(?:Thinking Process|Thought Process|Thought|Reasoning|Analysis)\s*:?\s*\n?/i, "")
22
+ .trim();
23
+ return reflowThought(cleaned);
24
+ }
25
+ function reflowThought(text) {
26
+ const out = [];
27
+ for (const raw of text.replace(/\r/g, "").split("\n")) {
28
+ const trimmed = raw.trim();
29
+ if (!trimmed) {
30
+ if (out.length > 0 && out[out.length - 1] !== "")
31
+ out.push("");
32
+ continue;
33
+ }
34
+ if (/^\d+\.\s/.test(trimmed)) {
35
+ out.push(trimmed);
36
+ continue;
37
+ }
38
+ if (/^[-*+]\s/.test(trimmed)) {
39
+ out.push(` ${trimmed}`);
40
+ continue;
41
+ }
42
+ if (out.length === 0 || out[out.length - 1] === "") {
43
+ out.push(trimmed);
44
+ continue;
45
+ }
46
+ out[out.length - 1] = `${out[out.length - 1]} ${trimmed}`;
47
+ }
48
+ while (out.length > 0 && out[out.length - 1] === "")
49
+ out.pop();
50
+ return out.join("\n");
51
+ }
52
+ function normalizeText(text) {
53
+ return text
54
+ .replace(/\r\n/g, "\n")
55
+ .replace(/\r/g, "")
56
+ .replace(/\n{3,}/g, "\n\n")
57
+ .replace(/[ \t]+$/gm, "")
58
+ .trim();
59
+ }
60
+ const formatElapsed = (ms) => ms < 1000 ? `${(ms / 1000).toFixed(1)}sn` : `${Math.round(ms / 1000)}sn`;
61
+ export const CHAT_COMMANDS = [
62
+ { name: "yeni", desc: "yeni oturum başlat" },
63
+ { name: "clear", desc: "konuşma geçmişini temizle" },
64
+ { name: "sessions", desc: "kayıtlı oturumları listele" },
65
+ { name: "resume", desc: "oturum yükle: /resume <id>" },
66
+ { name: "export", desc: "oturumu Markdown'a dök: /export [dosya]" },
67
+ { name: "kopyala", desc: "son araç çıktısını/yanıtı panoya kopyala" },
68
+ { name: "todo", desc: "görevler: /todo ekle <metin> | sil <no> | temizle | list" },
69
+ { name: "model", desc: "model yönetimi: list | install <id> | use <id>" },
70
+ { name: "cd", desc: "çalışma dizinini değiştir: /cd <dizin>" },
71
+ { name: "doctor", desc: "sistem kontrolü (GPU, model, SAC)" },
72
+ { name: "stats", desc: "bağlam ve oturum istatistikleri" },
73
+ { name: "tools", desc: "ajanın araçlarını listele" },
74
+ { name: "config", desc: "ayarları göster/değiştir: /config [anahtar değer]" },
75
+ { name: "exit", desc: "çıkış yap" },
76
+ { name: "help", desc: "komutları listele" }
77
+ ];
78
+ export class ChatTui {
79
+ options;
80
+ messages = [];
81
+ runState = "idle";
82
+ buffer = "";
83
+ cursor = 0;
84
+ chatScrollOffset = 0;
85
+ history = [];
86
+ historyIndex = -1;
87
+ frame = 0;
88
+ timer = null;
89
+ started = false;
90
+ loadPct = 0;
91
+ sessionTitle = "";
92
+ thinkingStartedAt = 0;
93
+ streaming = false;
94
+ stepCurrent = 0;
95
+ stepMax = 0;
96
+ contextInfo = null;
97
+ modelName = "";
98
+ gpuInfo = "";
99
+ cwdDisplay = "";
100
+ sidebarVisible = true;
101
+ clickTargets = [];
102
+ menuClickTargets = [];
103
+ todos = [];
104
+ rawBuf = "";
105
+ constructor(options) {
106
+ this.options = options;
107
+ }
108
+ get width() {
109
+ return process.stdout.columns ?? 80;
110
+ }
111
+ get height() {
112
+ return process.stdout.rows ?? 24;
113
+ }
114
+ start() {
115
+ if (this.started)
116
+ return;
117
+ this.started = true;
118
+ process.stdout.write("\x1b[?1049h\x1b[2J\x1b[H\x1b[?25l");
119
+ process.stdout.write("\x1b]0;AJAN - TakimCizgisi\x07");
120
+ process.stdout.write("\x1b[?1000h\x1b[?1006h");
121
+ process.once("exit", () => {
122
+ process.stdout.write("\x1b]0;\x07");
123
+ process.stdout.write("\x1b[?25h\x1b[0m\x1b[?1000l\x1b[?1006l\x1b[?1049l");
124
+ });
125
+ process.stdin.setRawMode(true);
126
+ process.stdin.on("data", this.onInputData);
127
+ process.stdout.on("resize", this.onResize);
128
+ this.timer = setInterval(() => {
129
+ this.frame++;
130
+ if (this.runState !== "idle" && !this.streaming)
131
+ this.render();
132
+ }, 100);
133
+ this.render();
134
+ }
135
+ stop() {
136
+ if (!this.started)
137
+ return;
138
+ this.started = false;
139
+ if (this.timer)
140
+ clearInterval(this.timer);
141
+ this.timer = null;
142
+ process.stdin.removeListener("data", this.onInputData);
143
+ process.stdout.removeListener("resize", this.onResize);
144
+ if (process.stdin.isTTY)
145
+ process.stdin.setRawMode(false);
146
+ process.stdout.write("\x1b]0;\x07");
147
+ process.stdout.write("\x1b[?25h\x1b[0m\x1b[?1000l\x1b[?1006l\x1b[?1049l");
148
+ }
149
+ onResize = () => {
150
+ this.render();
151
+ };
152
+ addUser(text) {
153
+ this.commitThinking();
154
+ this.messages.push({ kind: "user", text });
155
+ this.streaming = false;
156
+ this.render();
157
+ }
158
+ appendThinking(text) {
159
+ if (!text)
160
+ return;
161
+ if (this.thinkingStartedAt === 0)
162
+ this.thinkingStartedAt = Date.now();
163
+ const last = this.messages[this.messages.length - 1];
164
+ if (last && last.kind === "thought") {
165
+ last.text += text;
166
+ if (last.text.length > 12000)
167
+ last.text = last.text.slice(-12000);
168
+ }
169
+ else {
170
+ this.messages.push({ kind: "thought", text });
171
+ }
172
+ this.render();
173
+ }
174
+ appendAssistant(text) {
175
+ this.commitThinking();
176
+ this.streaming = true;
177
+ const last = this.messages[this.messages.length - 1];
178
+ if (last && last.kind === "assistant") {
179
+ last.text += text;
180
+ }
181
+ else {
182
+ this.messages.push({ kind: "assistant", text });
183
+ }
184
+ this.render();
185
+ }
186
+ addInfo(text) {
187
+ this.messages.push({ kind: "info", text });
188
+ this.render();
189
+ }
190
+ getMessages() {
191
+ return this.messages.map((m) => ({ ...m }));
192
+ }
193
+ loadMessages(messages) {
194
+ const list = [];
195
+ for (const raw of messages) {
196
+ if (!raw || typeof raw !== "object")
197
+ continue;
198
+ const kind = raw.kind;
199
+ if (kind === "user" || kind === "assistant") {
200
+ const text = String(raw.text ?? "");
201
+ if (text)
202
+ list.push({ kind, text });
203
+ }
204
+ else if (kind === "thought") {
205
+ const t = raw;
206
+ const text = String(t.text ?? "");
207
+ if (text) {
208
+ list.push({
209
+ kind: "thought",
210
+ text,
211
+ elapsed: t.elapsed != null ? Number(t.elapsed) : undefined,
212
+ expanded: t.expanded === true
213
+ });
214
+ }
215
+ }
216
+ else if (kind === "info") {
217
+ list.push({ kind: "info", text: String(raw.text ?? "") });
218
+ }
219
+ else if (kind === "tool") {
220
+ const t = raw;
221
+ list.push({
222
+ kind: "tool",
223
+ name: String(t.name ?? "?"),
224
+ state: t.state === "error" ? "error" : "done",
225
+ startedAt: Number(t.startedAt ?? Date.now()),
226
+ elapsed: Number(t.elapsed ?? 0),
227
+ output: t.output != null ? String(t.output) : undefined
228
+ });
229
+ }
230
+ }
231
+ this.messages = list;
232
+ this.thinkingStartedAt = 0;
233
+ this.streaming = false;
234
+ this.chatScrollOffset = 0;
235
+ this.render();
236
+ }
237
+ clearMessages() {
238
+ this.messages = [];
239
+ this.thinkingStartedAt = 0;
240
+ this.streaming = false;
241
+ this.chatScrollOffset = 0;
242
+ this.stepCurrent = 0;
243
+ this.stepMax = 0;
244
+ this.render();
245
+ }
246
+ setSessionTitle(title) {
247
+ this.sessionTitle = title;
248
+ this.render();
249
+ }
250
+ setModelInfo(name, gpu) {
251
+ this.modelName = name;
252
+ this.gpuInfo = gpu;
253
+ this.render();
254
+ }
255
+ setCwd(cwd) {
256
+ this.cwdDisplay = cwd;
257
+ this.render();
258
+ }
259
+ setContextInfo(info) {
260
+ this.contextInfo = info;
261
+ }
262
+ setStep(current, max) {
263
+ this.stepCurrent = current;
264
+ this.stepMax = max;
265
+ this.render();
266
+ }
267
+ toggleSidebar() {
268
+ this.sidebarVisible = !this.sidebarVisible;
269
+ this.render();
270
+ }
271
+ get sidebarShown() {
272
+ return this.sidebarVisible;
273
+ }
274
+ addTodo(text) {
275
+ const trimmed = text.trim();
276
+ if (!trimmed)
277
+ return;
278
+ this.todos.push({ text: trimmed, done: false });
279
+ this.render();
280
+ }
281
+ toggleTodo(index) {
282
+ const todo = this.todos[index];
283
+ if (todo) {
284
+ todo.done = !todo.done;
285
+ this.render();
286
+ }
287
+ }
288
+ removeTodo(index) {
289
+ if (index >= 0 && index < this.todos.length) {
290
+ this.todos.splice(index, 1);
291
+ this.render();
292
+ }
293
+ }
294
+ clearTodos() {
295
+ this.todos = [];
296
+ this.render();
297
+ }
298
+ setTodos(todos) {
299
+ this.todos = todos.map((t) => ({ text: String(t.text ?? ""), done: t.done === true }));
300
+ this.render();
301
+ }
302
+ getTodos() {
303
+ return this.todos.map((t) => ({ ...t }));
304
+ }
305
+ startTool(name) {
306
+ this.commitThinking();
307
+ this.messages.push({ kind: "tool", name, state: "running", startedAt: Date.now(), elapsed: 0 });
308
+ this.runState = "tool";
309
+ this.streaming = false;
310
+ this.render();
311
+ }
312
+ endTool(name, ok, output) {
313
+ for (let i = this.messages.length - 1; i >= 0; i--) {
314
+ const m = this.messages[i];
315
+ if (m && m.kind === "tool" && m.name === name && m.state === "running") {
316
+ m.state = ok ? "done" : "error";
317
+ m.elapsed = Date.now() - m.startedAt;
318
+ m.output = output;
319
+ m.expanded = !ok;
320
+ break;
321
+ }
322
+ }
323
+ if (this.runState === "tool")
324
+ this.runState = "thinking";
325
+ this.streaming = false;
326
+ this.render();
327
+ }
328
+ setLoading(pct) {
329
+ this.loadPct = pct;
330
+ this.runState = "loading";
331
+ this.streaming = false;
332
+ this.render();
333
+ }
334
+ setThinking() {
335
+ if (this.thinkingStartedAt === 0)
336
+ this.thinkingStartedAt = Date.now();
337
+ this.runState = "thinking";
338
+ this.streaming = false;
339
+ this.render();
340
+ }
341
+ endTurn() {
342
+ this.commitThinking();
343
+ for (const m of this.messages) {
344
+ if (m.kind === "tool" && m.state === "running") {
345
+ m.state = "error";
346
+ m.elapsed = Date.now() - m.startedAt;
347
+ }
348
+ }
349
+ this.runState = "idle";
350
+ this.streaming = false;
351
+ this.stepCurrent = 0;
352
+ this.render();
353
+ }
354
+ commitThinking() {
355
+ const last = this.messages[this.messages.length - 1];
356
+ if (last && last.kind === "thought" && last.elapsed === undefined && this.thinkingStartedAt !== 0) {
357
+ last.elapsed = Date.now() - this.thinkingStartedAt;
358
+ last.expanded = false;
359
+ }
360
+ this.thinkingStartedAt = 0;
361
+ }
362
+ onInputData = (chunk) => {
363
+ this.rawBuf += chunk.toString("utf8");
364
+ this.dispatchInput();
365
+ };
366
+ dispatchInput() {
367
+ while (this.rawBuf.length > 0) {
368
+ const c = this.rawBuf.charCodeAt(0);
369
+ if (c === 27) {
370
+ const mouse = this.rawBuf.match(/^\x1b\[<(\d+);(\d+);(\d+)([Mm])/);
371
+ if (mouse) {
372
+ const btn = Number(mouse[1]);
373
+ const x = Number(mouse[2]);
374
+ const y = Number(mouse[3]);
375
+ this.rawBuf = this.rawBuf.slice(mouse[0].length);
376
+ if (mouse[4] === "M") {
377
+ if (btn === 64)
378
+ this.scrollChat(WHEEL_STEP);
379
+ else if (btn === 65)
380
+ this.scrollChat(-WHEEL_STEP);
381
+ else if (btn === 0 || btn === 1 || btn === 2)
382
+ this.handleClick(x, y);
383
+ }
384
+ continue;
385
+ }
386
+ const csi = this.rawBuf.match(/^\x1b\[([0-9;]*)([A-Za-z~])/);
387
+ if (csi) {
388
+ this.rawBuf = this.rawBuf.slice(csi[0].length);
389
+ this.handleCSI(csi[1] ?? "", csi[2] ?? "");
390
+ continue;
391
+ }
392
+ const ss3 = this.rawBuf.match(/^\x1bO([A-Za-z])/);
393
+ if (ss3) {
394
+ this.rawBuf = this.rawBuf.slice(ss3[0].length);
395
+ this.handleCSI("", ss3[1] ?? "");
396
+ continue;
397
+ }
398
+ this.rawBuf = this.rawBuf.slice(1);
399
+ this.handleKey("", { name: "escape" });
400
+ continue;
401
+ }
402
+ if (c === 3) {
403
+ this.rawBuf = this.rawBuf.slice(1);
404
+ this.options.onExit();
405
+ continue;
406
+ }
407
+ if (c === 13 || c === 10) {
408
+ this.rawBuf = this.rawBuf.slice(1);
409
+ this.handleKey("", { name: "enter" });
410
+ continue;
411
+ }
412
+ if (c === 9) {
413
+ this.rawBuf = this.rawBuf.slice(1);
414
+ this.handleKey("", { name: "tab" });
415
+ continue;
416
+ }
417
+ if (c === 8 || c === 127) {
418
+ this.rawBuf = this.rawBuf.slice(1);
419
+ this.handleKey("", { name: "backspace" });
420
+ continue;
421
+ }
422
+ if (c < 32) {
423
+ this.rawBuf = this.rawBuf.slice(1);
424
+ continue;
425
+ }
426
+ let i = 1;
427
+ while (i < this.rawBuf.length) {
428
+ const cc = this.rawBuf.charCodeAt(i);
429
+ if (cc < 32 || cc === 127 || cc === 27)
430
+ break;
431
+ i++;
432
+ }
433
+ const s = this.rawBuf.slice(0, i);
434
+ this.rawBuf = this.rawBuf.slice(i);
435
+ this.handleKey(s, {});
436
+ continue;
437
+ }
438
+ }
439
+ handleCSI(code, final) {
440
+ switch (final) {
441
+ case "A":
442
+ this.handleKey("", { name: "up" });
443
+ return;
444
+ case "B":
445
+ this.handleKey("", { name: "down" });
446
+ return;
447
+ case "C":
448
+ this.handleKey("", { name: "right" });
449
+ return;
450
+ case "D":
451
+ this.handleKey("", { name: "left" });
452
+ return;
453
+ case "H":
454
+ this.handleKey("", { name: "home" });
455
+ return;
456
+ case "F":
457
+ this.handleKey("", { name: "end" });
458
+ return;
459
+ }
460
+ if (code === "3") {
461
+ this.handleKey("", { name: "delete" });
462
+ return;
463
+ }
464
+ if (code === "5") {
465
+ this.handleKey("", { name: "pageup" });
466
+ return;
467
+ }
468
+ if (code === "6") {
469
+ this.handleKey("", { name: "pagedown" });
470
+ return;
471
+ }
472
+ if (code === "1" || code === "7") {
473
+ this.handleKey("", { name: "home" });
474
+ return;
475
+ }
476
+ if (code === "4" || code === "8") {
477
+ this.handleKey("", { name: "end" });
478
+ return;
479
+ }
480
+ }
481
+ handleClick(x, y) {
482
+ const layout = this.computeLayout();
483
+ const rowIdx = y - 1;
484
+ if (rowIdx < 0 || rowIdx >= layout.height)
485
+ return;
486
+ if (layout.sidebarW > 0 && x <= layout.sidebarW) {
487
+ const todoIdx = this.menuClickTargets[rowIdx];
488
+ if (todoIdx != null && todoIdx >= 0) {
489
+ this.toggleTodo(todoIdx);
490
+ }
491
+ return;
492
+ }
493
+ const msg = this.clickTargets[rowIdx];
494
+ if (msg && msg.kind === "tool") {
495
+ msg.expanded = !msg.expanded;
496
+ this.render();
497
+ }
498
+ else if (msg && msg.kind === "thought") {
499
+ msg.expanded = msg.expanded === false;
500
+ this.render();
501
+ }
502
+ }
503
+ scrollChat(delta) {
504
+ this.chatScrollOffset = Math.max(0, this.chatScrollOffset + delta);
505
+ this.render();
506
+ }
507
+ handleKey = (str, key) => {
508
+ if (!key)
509
+ return;
510
+ if (key.ctrl && key.name === "c") {
511
+ this.options.onExit();
512
+ return;
513
+ }
514
+ switch (key.name) {
515
+ case "pageup": {
516
+ this.scrollChat(Math.max(1, this.height - 4));
517
+ return;
518
+ }
519
+ case "pagedown": {
520
+ this.scrollChat(-Math.max(1, this.height - 4));
521
+ return;
522
+ }
523
+ case "escape": {
524
+ if (this.runState !== "idle") {
525
+ this.options.onAbort();
526
+ return;
527
+ }
528
+ if (this.buffer.length > 0) {
529
+ this.buffer = "";
530
+ this.cursor = 0;
531
+ this.render();
532
+ }
533
+ return;
534
+ }
535
+ }
536
+ if (this.runState !== "idle")
537
+ return;
538
+ switch (key.name) {
539
+ case "tab": {
540
+ this.toggleSidebar();
541
+ return;
542
+ }
543
+ case "return":
544
+ case "enter": {
545
+ if (this.buffer.trim() === "") {
546
+ this.render();
547
+ return;
548
+ }
549
+ const trimmed = this.buffer.trim();
550
+ this.buffer = "";
551
+ this.cursor = 0;
552
+ this.historyIndex = -1;
553
+ if (trimmed.startsWith("/")) {
554
+ const space = trimmed.indexOf(" ");
555
+ const cmd = (space === -1 ? trimmed.slice(1) : trimmed.slice(1, space)).toLowerCase();
556
+ const arg = space === -1 ? "" : trimmed.slice(space + 1).trim();
557
+ if (this.options.onCommand) {
558
+ this.options.onCommand(cmd, arg);
559
+ return;
560
+ }
561
+ }
562
+ this.history.push(trimmed);
563
+ this.render();
564
+ this.options.onInput(trimmed);
565
+ return;
566
+ }
567
+ case "backspace": {
568
+ if (this.cursor > 0) {
569
+ this.buffer = this.buffer.slice(0, this.cursor - 1) + this.buffer.slice(this.cursor);
570
+ this.cursor--;
571
+ this.render();
572
+ }
573
+ return;
574
+ }
575
+ case "delete": {
576
+ if (this.cursor < this.buffer.length) {
577
+ this.buffer = this.buffer.slice(0, this.cursor) + this.buffer.slice(this.cursor + 1);
578
+ this.render();
579
+ }
580
+ return;
581
+ }
582
+ case "left": {
583
+ if (this.cursor > 0) {
584
+ this.cursor--;
585
+ this.render();
586
+ }
587
+ return;
588
+ }
589
+ case "right": {
590
+ if (this.cursor < this.buffer.length) {
591
+ this.cursor++;
592
+ this.render();
593
+ }
594
+ return;
595
+ }
596
+ case "home": {
597
+ this.cursor = 0;
598
+ this.render();
599
+ return;
600
+ }
601
+ case "end": {
602
+ this.cursor = this.buffer.length;
603
+ this.render();
604
+ return;
605
+ }
606
+ case "up": {
607
+ this.navigateHistory(1);
608
+ return;
609
+ }
610
+ case "down": {
611
+ this.navigateHistory(-1);
612
+ return;
613
+ }
614
+ }
615
+ if (typeof str === "string" && str.length > 0) {
616
+ const firstCode = str.charCodeAt(0);
617
+ if (firstCode < 32)
618
+ return;
619
+ this.buffer = this.buffer.slice(0, this.cursor) + str + this.buffer.slice(this.cursor);
620
+ this.cursor += str.length;
621
+ this.render();
622
+ }
623
+ };
624
+ navigateHistory(dir) {
625
+ if (dir === -1) {
626
+ if (this.historyIndex === -1)
627
+ return;
628
+ this.historyIndex--;
629
+ if (this.historyIndex === -1) {
630
+ this.buffer = "";
631
+ this.cursor = 0;
632
+ this.render();
633
+ return;
634
+ }
635
+ }
636
+ else {
637
+ if (this.history.length === 0)
638
+ return;
639
+ if (this.historyIndex + 1 >= this.history.length)
640
+ return;
641
+ this.historyIndex++;
642
+ }
643
+ const text = this.history[this.history.length - 1 - this.historyIndex];
644
+ this.buffer = text ?? "";
645
+ this.cursor = this.buffer.length;
646
+ this.render();
647
+ }
648
+ computeLayout() {
649
+ const width = Math.max(24, this.width);
650
+ const height = Math.max(6, this.height);
651
+ const wantSidebar = this.sidebarVisible && width >= SIDEBAR_MIN_COLS;
652
+ const sidebarW = wantSidebar ? SIDEBAR_WIDTH : 0;
653
+ const divider = sidebarW > 0 ? 1 : 0;
654
+ const chatW = width - sidebarW - divider;
655
+ return { width, height, sidebarW, chatX: sidebarW + divider, chatW };
656
+ }
657
+ render = () => {
658
+ if (!this.started)
659
+ return;
660
+ const layout = this.computeLayout();
661
+ const chatLines = [];
662
+ const owners = [];
663
+ for (const m of this.messages) {
664
+ const rendered = this.renderMessage(m, layout.chatW);
665
+ chatLines.push(...rendered);
666
+ for (let i = 0; i < rendered.length; i++)
667
+ owners.push(m);
668
+ }
669
+ const menu = this.commandMenu(layout.chatW);
670
+ const menuLines = menu?.length ?? 0;
671
+ const msgRows = Math.max(1, layout.height - 2 - menuLines);
672
+ const maxScroll = Math.max(0, chatLines.length - msgRows);
673
+ this.chatScrollOffset = Math.min(this.chatScrollOffset, maxScroll);
674
+ const offset = this.chatScrollOffset;
675
+ const startIdx = chatLines.length - msgRows - offset;
676
+ const sidebar = layout.sidebarW > 0 ? this.renderSidebar(layout.height, layout.sidebarW) : null;
677
+ const combine = (bodyLine) => sidebar ? this.combineRow(sidebar.line(outRows.length), bodyLine, layout.sidebarW, layout.width) : bodyLine;
678
+ const outRows = [];
679
+ this.clickTargets = [];
680
+ this.menuClickTargets = [];
681
+ outRows.push(this.renderStatus(layout.chatW));
682
+ this.clickTargets.push(null);
683
+ this.menuClickTargets.push(sidebar ? sidebar.click(0) : null);
684
+ for (let r = 0; r < msgRows; r++) {
685
+ const li = startIdx + r;
686
+ let bodyLine = "";
687
+ let owner = null;
688
+ if (li >= 0 && li < chatLines.length) {
689
+ bodyLine = chatLines[li] ?? "";
690
+ owner = owners[li] ?? null;
691
+ }
692
+ outRows.push(combine(bodyLine));
693
+ this.clickTargets.push(owner);
694
+ this.menuClickTargets.push(sidebar ? sidebar.click(outRows.length - 1) : null);
695
+ }
696
+ if (menu) {
697
+ for (let j = 0; j < menu.length; j++) {
698
+ outRows.push(combine(menu[j] ?? ""));
699
+ this.clickTargets.push(null);
700
+ this.menuClickTargets.push(sidebar ? sidebar.click(outRows.length - 1) : null);
701
+ }
702
+ }
703
+ const input = this.renderInput(layout.chatW);
704
+ outRows.push(combine(input.line));
705
+ this.clickTargets.push(null);
706
+ this.menuClickTargets.push(sidebar ? sidebar.click(outRows.length - 1) : null);
707
+ const out = ["\x1b[?25l\x1b[H"];
708
+ for (let i = 0; i < outRows.length; i++) {
709
+ out.push(outRows[i] ?? "");
710
+ out.push("\x1b[K");
711
+ if (i < outRows.length - 1)
712
+ out.push("\r\n");
713
+ }
714
+ const cursorCol = layout.chatX + input.cursorCol + 1;
715
+ out.push(`\x1b[${Math.min(layout.height, outRows.length)};${cursorCol}H\x1b[?25h`);
716
+ process.stdout.write(out.join(""));
717
+ };
718
+ renderSidebar(height, w) {
719
+ const lines = [];
720
+ const clicks = [];
721
+ const push = (text, target = null) => {
722
+ lines.push(this.padTo(text, w));
723
+ clicks.push(target);
724
+ };
725
+ push(chalk.bold.cyan("AJAN"));
726
+ push(chalk.dim("─".repeat(Math.max(1, w - 1))));
727
+ push(chalk.dim("Oturum"));
728
+ push(chalk.cyanBright(this.fitText(this.sessionTitle || "Yeni Sohbet", w - 1)));
729
+ push("");
730
+ push(chalk.dim("Sistem"));
731
+ if (this.modelName)
732
+ push(chalk.white(`model: ${chalk.reset(this.fitText(this.modelName, w - 7))}`));
733
+ if (this.gpuInfo)
734
+ push(chalk.white(`gpu: ${chalk.reset(this.fitText(this.gpuInfo, w - 6))}`));
735
+ if (this.cwdDisplay) {
736
+ const home = homedir();
737
+ const disp = this.cwdDisplay.startsWith(home) ? "~" + this.cwdDisplay.slice(home.length) : this.cwdDisplay;
738
+ const last = disp.split(/[\\/]/).filter(Boolean).pop() ?? disp;
739
+ push(chalk.white(`dizin: ${chalk.reset(this.fitText(last, w - 7))}`));
740
+ push(chalk.dim(` (${this.fitText(disp, w - 3)})`));
741
+ }
742
+ if (this.contextInfo && this.contextInfo.contextSize > 0) {
743
+ const pct = Math.min(100, Math.round((this.contextInfo.usedTokens / this.contextInfo.contextSize) * 100));
744
+ const cells = Math.max(4, w - 12);
745
+ const filled = Math.round((cells * pct) / 100);
746
+ const bar = chalk.green("█".repeat(filled)) + chalk.dim("░".repeat(cells - filled));
747
+ push(`${bar}`);
748
+ push(chalk.dim(`bağlam %${pct} (${this.contextInfo.usedTokens})`));
749
+ }
750
+ if (this.stepMax > 0) {
751
+ push(chalk.magenta(`adım: ${this.stepCurrent}/${this.stepMax}`));
752
+ }
753
+ push("");
754
+ push(chalk.dim("Görevler"));
755
+ if (this.todos.length === 0) {
756
+ push(chalk.dim("/todo ekle …"));
757
+ }
758
+ else {
759
+ for (let i = 0; i < this.todos.length; i++) {
760
+ if (lines.length >= height - 1)
761
+ break;
762
+ const t = this.todos[i];
763
+ const box = t.done ? chalk.green("✓") : chalk.dim("□");
764
+ const num = String(i + 1).padStart(2);
765
+ const text = t.done ? chalk.strikethrough(chalk.dim(t.text)) : t.text;
766
+ push(` ${box}${num} ${text.slice(0, Math.max(4, w - 6))}`, i);
767
+ }
768
+ }
769
+ while (lines.length < height)
770
+ push("");
771
+ lines.length = height;
772
+ clicks.length = height;
773
+ return {
774
+ line: (row) => lines[row] ?? this.padTo("", w),
775
+ click: (row) => clicks[row] ?? null
776
+ };
777
+ }
778
+ combineRow(menu, body, menuW, totalW) {
779
+ const avail = totalW - menuW - 1;
780
+ const trimmed = body.length === 0 ? "" : (this.splitStyledLine(body, avail)[0] ?? "");
781
+ return menu + "│" + trimmed;
782
+ }
783
+ /** Metni belirtilen genişliğe üç nokta ile sığdırır */
784
+ fitText(text, max) {
785
+ if (text.length <= max)
786
+ return text;
787
+ return text.slice(0, Math.max(1, max - 1)) + "…";
788
+ }
789
+ padTo(text, w) {
790
+ const visible = text.replace(ANSI_RE, "").length;
791
+ if (visible >= w)
792
+ return text;
793
+ return text + " ".repeat(w - visible);
794
+ }
795
+ commandMenu(width) {
796
+ const line = this.buffer.trimStart();
797
+ if (!line.startsWith("/"))
798
+ return null;
799
+ const space = line.indexOf(" ");
800
+ const term = (space === -1 ? line.slice(1) : line.slice(1, space)).toLowerCase();
801
+ const matching = CHAT_COMMANDS.filter((c) => c.name.startsWith(term));
802
+ if (matching.length === 0)
803
+ return null;
804
+ const lines = [];
805
+ for (const c of matching) {
806
+ const name = `/${c.name}`;
807
+ const colored = c.name === term ? chalk.cyan(name) : chalk.cyanBright(name);
808
+ const pad = " ".repeat(Math.max(1, 10 - name.length));
809
+ const avail = Math.max(4, width - 2 - name.length - pad.length);
810
+ const desc = c.desc.length > avail ? `${c.desc.slice(0, avail - 1)}…` : c.desc;
811
+ lines.push(` ${colored}${pad}${chalk.dim(desc)}`);
812
+ }
813
+ return lines;
814
+ }
815
+ renderStatus(chatW) {
816
+ const frame = SPINNER_FRAMES[this.frame % SPINNER_FRAMES.length] ?? SPINNER_FRAMES[0];
817
+ const tag = chalk.magenta("[AJAN]");
818
+ let text;
819
+ switch (this.runState) {
820
+ case "loading": {
821
+ const pct = Math.round(this.loadPct);
822
+ const label = pct >= 100 ? "Sistemler hazır" :
823
+ pct <= 0 ? "Başlatılıyor…" :
824
+ `Yükleniyor… %${pct}`;
825
+ text = `${tag} ${frame} ${label}`;
826
+ break;
827
+ }
828
+ case "thinking":
829
+ text = this.streaming
830
+ ? `${tag} yanıt yazılıyor…`
831
+ : `${tag} ${frame} düşünüyor…`;
832
+ break;
833
+ case "tool":
834
+ text = `${tag} ${frame} araç çalışıyor…`;
835
+ break;
836
+ default: {
837
+ const scrolled = this.chatScrollOffset > 0 ? chalk.yellow(" ↓kayıt ") : "";
838
+ text = `${tag}${scrolled} ⏎ gönder · / komut · TAB panel · ESC durdur · PgUp/PgDn kaydır`;
839
+ }
840
+ }
841
+ return this.truncateVisible(text, chatW);
842
+ }
843
+ truncateVisible(text, width) {
844
+ if (text.replace(ANSI_RE, "").length <= width)
845
+ return text;
846
+ const parts = this.splitStyledLine(text, Math.max(1, width));
847
+ return parts[0] ?? "";
848
+ }
849
+ renderMessage(m, width) {
850
+ switch (m.kind) {
851
+ case "user":
852
+ return this.wrapColored(chalk.cyan("siz> "), this.renderMd(m.text), width);
853
+ case "assistant":
854
+ return this.wrapStyled(chalk.green("ajan> "), this.renderMd(m.text), width);
855
+ case "thought": {
856
+ const done = m.elapsed !== undefined;
857
+ const elapsed = done ? formatElapsed(m.elapsed ?? 0) : null;
858
+ const frame = done ? "" : ` ${SPINNER_FRAMES[this.frame % SPINNER_FRAMES.length] ?? ""}`;
859
+ const status = done ? `${elapsed} | Düşünme Tamamlandı` : `${frame} düşünüyor…`;
860
+ const header = ` ${chalk.gray(`[Düşünce] ${status}`)}`;
861
+ if (done && !m.expanded) {
862
+ return [`${header}${chalk.dim(" · tıkla göster")}`];
863
+ }
864
+ const lines = [header];
865
+ const content = cleanThinking(m.text);
866
+ const preview = content.length > 2000 ? `${content.slice(0, 2000)}\n… (düşünce kesildi)` : content;
867
+ for (const raw of preview.split("\n")) {
868
+ if (!raw.trim())
869
+ continue;
870
+ lines.push(...this.wrapColored(" ", chalk.gray(this.renderMd(raw)), width));
871
+ }
872
+ return lines;
873
+ }
874
+ case "info":
875
+ return this.wrapColored("", chalk.dim(this.renderMd(m.text)), width);
876
+ case "tool": {
877
+ const label = m.name ?? "?";
878
+ if (m.state === "running") {
879
+ const bar = BAR_FRAMES[this.frame % BAR_FRAMES.length] ?? BAR_FRAMES[0];
880
+ return [` ${chalk.yellow(`[${label}] ${bar}`)}`];
881
+ }
882
+ const elapsed = formatElapsed(m.elapsed ?? 0);
883
+ const txt = m.state === "done" ? `${elapsed} bitirdi` : `${elapsed} hata`;
884
+ const color = m.state === "done" ? chalk.green : chalk.red;
885
+ const hint = m.state === "done" && m.output && !m.expanded ? ` ${chalk.dim("· tıkla göster")}` : "";
886
+ const lines = [` ${color(`[${label}] ${txt}`)}${hint}`];
887
+ if (m.output && (m.expanded || m.state === "error")) {
888
+ const preview = m.output.replace(/\r/g, "").split("\n").slice(0, 10).join("\n");
889
+ const truncated = preview.length > 800 ? `${preview.slice(0, 800)}\n…` : preview;
890
+ for (const line of truncated.split("\n")) {
891
+ lines.push(...this.wrapColored(" ", chalk.dim(this.renderMd(line)), width));
892
+ }
893
+ }
894
+ return lines;
895
+ }
896
+ }
897
+ }
898
+ renderInput(width) {
899
+ const prompt = "siz> ";
900
+ const avail = Math.max(1, width - prompt.length);
901
+ let start = 0;
902
+ if (this.buffer.length > avail) {
903
+ start = this.buffer.length - avail;
904
+ if (this.cursor < start)
905
+ start = this.cursor;
906
+ else if (this.cursor - start > avail)
907
+ start = this.cursor - avail;
908
+ }
909
+ const shown = this.buffer.slice(start, start + avail);
910
+ const cursorCol = prompt.length + (this.cursor - start);
911
+ return { line: prompt + shown, cursorCol };
912
+ }
913
+ wrapColored(prefix, text, width) {
914
+ const clean = text.replace(ANSI_RE, "").replace(/\r\n/g, "\n").replace(/\r/g, "");
915
+ const contPrefix = " ".repeat(prefix.length);
916
+ const prefixed = [];
917
+ const rawLines = clean.split("\n");
918
+ for (let i = 0; i < rawLines.length; i++) {
919
+ const raw = rawLines[i] ?? "";
920
+ prefixed.push(i === 0 ? prefix + raw : contPrefix + raw);
921
+ }
922
+ const wrapped = [];
923
+ for (const line of prefixed) {
924
+ let cur = line;
925
+ while (cur.length > width) {
926
+ wrapped.push(cur.slice(0, width));
927
+ cur = cur.slice(width);
928
+ }
929
+ if (cur.length > 0 || wrapped.length === 0)
930
+ wrapped.push(cur);
931
+ }
932
+ if (wrapped.length === 0)
933
+ wrapped.push("");
934
+ return wrapped;
935
+ }
936
+ renderMd(text) {
937
+ const styledLine = (line) => {
938
+ let s = line;
939
+ s = s.replace(/\*\*([^*]+)\*\*/g, (_m, p1) => chalk.bold(p1));
940
+ s = s.replace(/(^|[^*])\*([^*\n]+)\*/g, (_m, p1, p2) => `${p1}${chalk.italic(p2)}`);
941
+ s = s.replace(/`([^`\n]+)`/g, (_m, p1) => chalk.cyan(p1));
942
+ s = s.replace(/^#{1,6}\s+(.+)$/gm, (_m, p1) => chalk.bold(p1));
943
+ return s.replace(/\*\*/g, "").replace(/\*([^*\n]+)\*/g, "$1");
944
+ };
945
+ const lines = normalizeText(text).split("\n");
946
+ const dedented = [];
947
+ let inFence = false;
948
+ for (const raw of lines) {
949
+ if (/^\s*(```|~~~)/.test(raw)) {
950
+ inFence = !inFence;
951
+ dedented.push(raw);
952
+ continue;
953
+ }
954
+ dedented.push(inFence ? raw : raw.trimStart());
955
+ }
956
+ return dedented.map(styledLine).join("\n");
957
+ }
958
+ splitStyledLine(line, width) {
959
+ const parts = [];
960
+ let cur = "";
961
+ let visible = 0;
962
+ let i = 0;
963
+ const n = line.length;
964
+ while (i < n) {
965
+ if (line[i] === "\x1b" && line[i + 1] === "[") {
966
+ const m = /^\x1b\[[0-9;]*[A-Za-z]/.exec(line.slice(i));
967
+ if (m) {
968
+ cur += m[0];
969
+ i += m[0].length;
970
+ continue;
971
+ }
972
+ }
973
+ if (visible === width) {
974
+ parts.push(cur);
975
+ cur = "";
976
+ visible = 0;
977
+ continue;
978
+ }
979
+ cur += line[i];
980
+ visible++;
981
+ i++;
982
+ }
983
+ parts.push(cur);
984
+ if (parts.length === 0)
985
+ parts.push("");
986
+ return parts;
987
+ }
988
+ wrapStyled(prefix, styled, width) {
989
+ const contPrefix = " ".repeat(prefix.length);
990
+ const out = [];
991
+ const rawLines = styled.replace(/\r\n/g, "\n").replace(/\r/g, "").split("\n");
992
+ for (let i = 0; i < rawLines.length; i++) {
993
+ const line = rawLines[i] ?? "";
994
+ const first = i === 0 ? prefix + line : contPrefix + line;
995
+ for (const chunk of this.splitStyledLine(first, width))
996
+ out.push(chunk);
997
+ }
998
+ if (out.length === 0)
999
+ out.push("");
1000
+ return out;
1001
+ }
1002
+ }
1003
+ //# sourceMappingURL=tui.js.map