@danieltmn/tamnora 0.24.1 → 0.25.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 (50) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/harness/packages/server/public/app.js +833 -834
  3. package/harness/packages/server/public/appearance.js +4 -0
  4. package/harness/packages/server/public/boot.js +49 -0
  5. package/harness/packages/server/public/chat.js +17 -0
  6. package/harness/packages/server/public/chrome.js +855 -852
  7. package/harness/packages/server/public/commands.js +448 -403
  8. package/harness/packages/server/public/core.js +229 -216
  9. package/harness/packages/server/public/favicon.ico +0 -0
  10. package/harness/packages/server/public/icons/icon-128x128.png +0 -0
  11. package/harness/packages/server/public/icons/icon-144x144.png +0 -0
  12. package/harness/packages/server/public/icons/icon-152x152.png +0 -0
  13. package/harness/packages/server/public/icons/icon-192x192.png +0 -0
  14. package/harness/packages/server/public/icons/icon-384x384.png +0 -0
  15. package/harness/packages/server/public/icons/icon-512x512.png +0 -0
  16. package/harness/packages/server/public/icons/icon-72x72.png +0 -0
  17. package/harness/packages/server/public/icons/icon-96x96.png +0 -0
  18. package/harness/packages/server/public/icons/logo.png +0 -0
  19. package/harness/packages/server/public/icons/logo_dark.png +0 -0
  20. package/harness/packages/server/public/icons/logo_light.png +0 -0
  21. package/harness/packages/server/public/icons/tamnora.ico +0 -0
  22. package/harness/packages/server/public/index.html +72 -101
  23. package/harness/packages/server/public/landing/boot.js +16 -0
  24. package/harness/packages/server/public/landing/index.html +123 -59
  25. package/harness/packages/server/public/landing/landing.js +11 -9
  26. package/harness/packages/server/public/logo.png +0 -0
  27. package/harness/packages/server/public/markdown.js +1 -1
  28. package/harness/packages/server/public/settings.js +1155 -1080
  29. package/harness/packages/server/public/settingsNav.js +81 -0
  30. package/harness/packages/server/public/styles.css +2818 -2515
  31. package/harness/packages/server/public/system.js +495 -495
  32. package/harness/packages/server/src/lib/appearance.js +4 -0
  33. package/harness/packages/server/src/lib/restart.js +20 -0
  34. package/harness/packages/server/src/lib/securityHeaders.js +53 -0
  35. package/harness/packages/server/src/routes/reload.js +46 -0
  36. package/harness/packages/server/src/server.js +6 -1
  37. package/harness/packages/server/test/accessibility.test.js +72 -0
  38. package/harness/packages/server/test/appearance.test.js +4 -0
  39. package/harness/packages/server/test/consoleModules.test.js +133 -57
  40. package/harness/packages/server/test/consoleTabs.test.js +228 -0
  41. package/harness/packages/server/test/docsSync.test.js +73 -0
  42. package/harness/packages/server/test/landingLogin.test.js +89 -0
  43. package/harness/packages/server/test/markdownSecurity.test.js +80 -0
  44. package/harness/packages/server/test/reload.test.js +95 -0
  45. package/harness/packages/server/test/securityHeaders.test.js +109 -0
  46. package/harness/packages/server/test/settingsNav.test.js +90 -0
  47. package/harness/packages/server/test/themeCss.js +110 -0
  48. package/harness/packages/server/test/themes.test.js +72 -0
  49. package/harness/scripts/restart.mjs +101 -0
  50. package/package.json +1 -1
@@ -12,10 +12,14 @@ export const THEMES = [
12
12
  { id: 'solarized-dark', label: 'Solarized oscuro', scheme: 'dark', metaColor: '#002b36' },
13
13
  { id: 'bosque', label: 'Bosque', scheme: 'light', metaColor: '#f2f4ec' },
14
14
  { id: 'vino', label: 'Vino', scheme: 'light', metaColor: '#f7eeee' },
15
+ { id: 'arena', label: 'Arena', scheme: 'light', metaColor: '#f6efdd' },
16
+ { id: 'hielo', label: 'Hielo', scheme: 'light', metaColor: '#edf3f7' },
15
17
  { id: 'vercel', label: 'Vercel', scheme: 'dark', metaColor: '#000000' },
16
18
  { id: 'dark-plus', label: 'Dark+', scheme: 'dark', metaColor: '#1e1e1e' },
17
19
  { id: 'github', label: 'GitHub', scheme: 'light', metaColor: '#ffffff' },
18
20
  { id: 'github-dark', label: 'GitHub oscuro', scheme: 'dark', metaColor: '#0d1117' },
21
+ { id: 'mocha', label: 'Mocha', scheme: 'dark', metaColor: '#211912' },
22
+ { id: 'ebano', label: 'Ébano', scheme: 'dark', metaColor: '#14191f' },
19
23
  ];
20
24
 
21
25
  export const FONT_PRESETS = [
@@ -0,0 +1,49 @@
1
+ (function () {
2
+ var root = document.documentElement;
3
+ var THEMES = ['paper', 'dark', 'midnight', 'nord', 'dracula', 'solarized', 'solarized-dark', 'bosque', 'vino', 'arena', 'hielo', 'vercel', 'dark-plus', 'github', 'github-dark', 'mocha', 'ebano'];
4
+ var DARK = ['dark', 'midnight', 'nord', 'dracula', 'solarized-dark', 'vercel', 'dark-plus', 'github-dark', 'mocha', 'ebano'];
5
+ var appearance = null;
6
+ try {
7
+ appearance = JSON.parse(localStorage.getItem('tamnora-console-appearance') || 'null');
8
+ } catch {
9
+ appearance = null;
10
+ }
11
+ var theme = appearance && appearance.theme;
12
+ if (!theme) {
13
+ try {
14
+ theme = localStorage.getItem('tamnora-console-theme');
15
+ } catch {
16
+ theme = null;
17
+ }
18
+ }
19
+ if (theme === 'light') theme = 'paper';
20
+ if (THEMES.indexOf(theme) < 0) {
21
+ theme = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'paper';
22
+ }
23
+ root.dataset.theme = theme;
24
+ root.dataset.scheme = DARK.indexOf(theme) >= 0 ? 'dark' : 'light';
25
+ // La consola resuelve el acceso con /api/auth/me; mientras tanto, oculta.
26
+ root.dataset.auth = 'pending';
27
+ if (!appearance) return;
28
+ if (appearance.fontCss) {
29
+ root.style.setProperty('--sans', appearance.fontCss.sans);
30
+ root.style.setProperty('--serif', appearance.fontCss.serif);
31
+ root.style.setProperty('--mono', appearance.fontCss.mono);
32
+ root.style.setProperty('--font', 'var(--sans)');
33
+ }
34
+ var href = appearance.google ? 'https://fonts.googleapis.com/css2?' + appearance.google + '&display=swap' : null;
35
+ var flink = document.getElementById('tamnora-fonts');
36
+ if (!href) {
37
+ if (flink) flink.remove();
38
+ } else if (!flink) {
39
+ flink = document.createElement('link');
40
+ flink.rel = 'stylesheet';
41
+ flink.id = 'tamnora-fonts';
42
+ flink.href = href;
43
+ document.head.appendChild(flink);
44
+ } else if (flink.getAttribute('href') !== href) {
45
+ flink.setAttribute('href', href);
46
+ }
47
+ if (appearance.chatFont) root.style.setProperty('--chat-font', appearance.chatFont + 'px');
48
+ if (appearance.scale) root.style.zoom = String(appearance.scale);
49
+ })();
@@ -103,6 +103,16 @@ export const panesEl = $('#panes');
103
103
  export const tabbarEl = $('#tabbar');
104
104
  export const chatForm = $('#chat-form');
105
105
  export const chatInput = $('#chat-input');
106
+
107
+ /**
108
+ * Ajusta el alto del textarea al contenido: crece hacia arriba a medida que se
109
+ * escribe, hasta el `max-height` del CSS (donde pasa a scrollear por dentro).
110
+ * Hay que llamarlo tambien al vaciarlo por codigo, o quedaria alto y vacio.
111
+ */
112
+ export function autoGrowChatInput() {
113
+ chatInput.style.height = 'auto';
114
+ chatInput.style.height = `${chatInput.scrollHeight}px`;
115
+ }
106
116
  export const submitBtn = chatForm.querySelector('button[type="submit"]');
107
117
  export const stopBtn = $('#stop-btn');
108
118
  export const interruptBtn = $('#interrupt-btn');
@@ -305,6 +315,7 @@ export async function regenerate(message) {
305
315
  await api(`/api/sessions/${id}/truncate`, { method: 'POST', body: { fromId: lastUser.id } }).catch(() => {});
306
316
  await reloadPane(id);
307
317
  chatInput.value = userText(lastUser.content);
318
+ autoGrowChatInput();
308
319
  await sendMessage();
309
320
  }
310
321
 
@@ -338,6 +349,7 @@ export async function undoTurn(message) {
338
349
 
339
350
  export function editMessage(message) { const id = messageId(message);
340
351
  chatInput.value = message.textEl.textContent;
352
+ autoGrowChatInput();
341
353
  chatInput.focus();
342
354
  state.pendingEdit = id ? { fromId: id } : null;
343
355
  }
@@ -753,6 +765,7 @@ export function addContinueButton(turn, data) {
753
765
  btn.addEventListener('click', () => {
754
766
  if (state.panes.get(turn.sessionKey)?.working) return;
755
767
  chatInput.value = 'Continuá donde quedaste.';
768
+ autoGrowChatInput();
756
769
  sendMessage();
757
770
  });
758
771
  turn.assistant().tools.appendChild(btn);
@@ -1195,6 +1208,7 @@ export async function sendMessage() {
1195
1208
  chatInput.value = '';
1196
1209
  state.attachments = [];
1197
1210
  renderAttachments();
1211
+ autoGrowChatInput();
1198
1212
  if (command) runShell(command);
1199
1213
  return;
1200
1214
  }
@@ -1224,6 +1238,7 @@ export async function sendMessage() {
1224
1238
  chatInput.value = '';
1225
1239
  state.attachments = [];
1226
1240
  renderAttachments();
1241
+ autoGrowChatInput();
1227
1242
  syncInterruptBtn();
1228
1243
  return;
1229
1244
  }
@@ -1240,6 +1255,7 @@ export async function sendMessage() {
1240
1255
  chatInput.value = '';
1241
1256
  state.attachments = [];
1242
1257
  renderAttachments();
1258
+ autoGrowChatInput();
1243
1259
  await startTurn(state.sessionId, activePane, input, images);
1244
1260
  }
1245
1261
 
@@ -1275,6 +1291,7 @@ export function sendInterrupt() {
1275
1291
  chatInput.value = '';
1276
1292
  state.attachments = [];
1277
1293
  renderAttachments();
1294
+ autoGrowChatInput();
1278
1295
  syncInterruptBtn();
1279
1296
  interruptAndSend(state.sessionId, input, images);
1280
1297
  }