@danieltmn/tamnora 0.9.5 → 0.9.6
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/CHANGELOG.md +8 -0
- package/harness/packages/cli/src/commands/autostart.js +5 -1
- package/harness/packages/server/public/app.js +122 -16
- package/harness/packages/server/public/favicon.ico +0 -0
- package/harness/packages/server/public/icons/icon-128x128.png +0 -0
- package/harness/packages/server/public/icons/icon-144x144.png +0 -0
- package/harness/packages/server/public/icons/icon-152x152.png +0 -0
- package/harness/packages/server/public/icons/icon-192x192.png +0 -0
- package/harness/packages/server/public/icons/icon-384x384.png +0 -0
- package/harness/packages/server/public/icons/icon-512x512.png +0 -0
- package/harness/packages/server/public/icons/icon-72x72.png +0 -0
- package/harness/packages/server/public/icons/icon-96x96.png +0 -0
- package/harness/packages/server/public/icons/logo.png +0 -0
- package/harness/packages/server/public/icons/tamnora.ico +0 -0
- package/harness/packages/server/public/index.html +63 -28
- package/harness/packages/server/public/landing/index.html +8 -4
- package/harness/packages/server/public/logo.png +0 -0
- package/harness/packages/server/public/manifest.webmanifest +20 -0
- package/harness/packages/server/public/styles.css +1772 -1667
- package/harness/packages/server/src/static.js +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,14 @@ Todos los cambios relevantes de este proyecto se documentan en este archivo.
|
|
|
5
5
|
El formato sigue [Keep a Changelog](https://keepachangelog.com/es-ES/1.1.0/)
|
|
6
6
|
y el proyecto usa [Versionado Semantico](https://semver.org/lang/es/).
|
|
7
7
|
|
|
8
|
+
## [0.9.6] - 2026-10-08
|
|
9
|
+
|
|
10
|
+
### Cambios
|
|
11
|
+
|
|
12
|
+
- fix(consola): borde inferior y padding inferior en la barra de pestañas
|
|
13
|
+
- feat(consola): iconos/logo (favicon, PWA, topbar) y composer estilo codex
|
|
14
|
+
- docs(website): voz, asesor y actualizacion con un click
|
|
15
|
+
|
|
8
16
|
## [0.9.5] - 2026-10-08
|
|
9
17
|
|
|
10
18
|
### Agregado
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
|
-
import { mkdirSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { existsSync, mkdirSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
5
6
|
import { homeDir } from '../../../core/src/index.js';
|
|
6
7
|
import { parse } from '../lib/util.js';
|
|
7
8
|
|
|
9
|
+
const ICON_PATH = fileURLToPath(new URL('../../../server/public/icons/tamnora.ico', import.meta.url));
|
|
10
|
+
|
|
8
11
|
const OPTIONS = { home: { type: 'string' }, help: { type: 'boolean', short: 'h' } };
|
|
9
12
|
|
|
10
13
|
function autostartPath() {
|
|
@@ -76,6 +79,7 @@ export async function autostartCommand(argv) {
|
|
|
76
79
|
`$sc.TargetPath = ${psQuote(wscript)};`,
|
|
77
80
|
`$sc.Arguments = ${psQuote(`"${vbsPath}"`)};`,
|
|
78
81
|
`$sc.WorkingDirectory = ${psQuote(workdir)};`,
|
|
82
|
+
existsSync(ICON_PATH) ? `$sc.IconLocation = ${psQuote(ICON_PATH)};` : '',
|
|
79
83
|
'$sc.WindowStyle = 1;',
|
|
80
84
|
'$sc.Save();',
|
|
81
85
|
].join(' ');
|
|
@@ -39,6 +39,7 @@ function setCwd(value) {
|
|
|
39
39
|
if (!value) return;
|
|
40
40
|
state.cwd = value;
|
|
41
41
|
$('#rail-cwd').textContent = value;
|
|
42
|
+
renderCwdPill();
|
|
42
43
|
if (state.explorerOpen) refreshExplorerMode();
|
|
43
44
|
}
|
|
44
45
|
|
|
@@ -478,11 +479,6 @@ async function loadModes() {
|
|
|
478
479
|
updateStatus();
|
|
479
480
|
}
|
|
480
481
|
|
|
481
|
-
function modeLabel(id) {
|
|
482
|
-
const option = [...modeSelect.options].find((o) => o.value === id);
|
|
483
|
-
return option ? option.textContent : id;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
482
|
async function setMode(id, { persist = true } = {}) {
|
|
487
483
|
if (![...modeSelect.options].some((o) => o.value === id)) return;
|
|
488
484
|
modeSelect.value = id;
|
|
@@ -504,11 +500,82 @@ document.addEventListener('keydown', (event) => {
|
|
|
504
500
|
}
|
|
505
501
|
});
|
|
506
502
|
|
|
503
|
+
/* --- pills del composer (carpeta, permisos, modelo) ----------------------- */
|
|
504
|
+
|
|
505
|
+
const cwdPill = $('#cwd-pill');
|
|
506
|
+
const cwdPillLabel = $('#cwd-pill-label');
|
|
507
|
+
const modePill = $('#mode-pill');
|
|
508
|
+
const modePillLabel = $('#mode-pill-label');
|
|
509
|
+
const modeMenu = $('#mode-menu');
|
|
510
|
+
const modelPill = $('#model-pill');
|
|
511
|
+
const modelPillLabel = $('#model-pill-label');
|
|
512
|
+
|
|
513
|
+
function renderCwdPill() {
|
|
514
|
+
if (!cwdPillLabel) return;
|
|
515
|
+
cwdPillLabel.textContent = state.cwd ? basename(state.cwd) : 'sin carpeta';
|
|
516
|
+
if (cwdPill) cwdPill.title = state.cwd || 'sin carpeta de trabajo';
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// El pill de permisos resume modo + auto-aprobación en uno de tres niveles
|
|
520
|
+
// estilo Codex: Read Only, Workspace Write y Full access.
|
|
521
|
+
function renderModePill() {
|
|
522
|
+
const plan = modeSelect.value === 'plan';
|
|
523
|
+
const full = !plan && Boolean(yesInput?.checked);
|
|
524
|
+
if (modePillLabel) modePillLabel.textContent = plan ? 'Read Only' : full ? 'Full access' : 'Workspace Write';
|
|
525
|
+
if (!modeMenu) return;
|
|
526
|
+
for (const item of modeMenu.querySelectorAll('.ctrl-menu-item')) {
|
|
527
|
+
const kind = item.dataset.kind;
|
|
528
|
+
const active = kind === 'read' ? plan : kind === 'full' ? full : !plan && !full;
|
|
529
|
+
item.classList.toggle('active', active);
|
|
530
|
+
item.setAttribute('aria-checked', active ? 'true' : 'false');
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
function renderModelPill() {
|
|
535
|
+
if (!modelPillLabel) return;
|
|
536
|
+
modelPillLabel.textContent = state.active.model || 'elegí un modelo';
|
|
537
|
+
if (modelPill) modelPill.title = `${state.active.provider || '—'} / ${state.active.model || '—'}`;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function closeModeMenu() {
|
|
541
|
+
if (!modeMenu || modeMenu.hidden) return;
|
|
542
|
+
modeMenu.hidden = true;
|
|
543
|
+
modePill?.setAttribute('aria-expanded', 'false');
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
cwdPill?.addEventListener('click', () => openFolderPicker('session'));
|
|
547
|
+
modePill?.addEventListener('click', (event) => {
|
|
548
|
+
event.stopPropagation();
|
|
549
|
+
if (!modeMenu) return;
|
|
550
|
+
modeMenu.hidden = !modeMenu.hidden;
|
|
551
|
+
modePill.setAttribute('aria-expanded', modeMenu.hidden ? 'false' : 'true');
|
|
552
|
+
});
|
|
553
|
+
modeMenu?.addEventListener('click', async (event) => {
|
|
554
|
+
const item = event.target.closest('.ctrl-menu-item');
|
|
555
|
+
if (!item) return;
|
|
556
|
+
const kind = item.dataset.kind;
|
|
557
|
+
closeModeMenu();
|
|
558
|
+
if (kind === 'full') {
|
|
559
|
+
await setMode('agent');
|
|
560
|
+
if (yesInput) yesInput.checked = true;
|
|
561
|
+
} else {
|
|
562
|
+
if (yesInput) yesInput.checked = false;
|
|
563
|
+
await setMode(kind === 'read' ? 'plan' : 'agent');
|
|
564
|
+
}
|
|
565
|
+
renderModePill();
|
|
566
|
+
});
|
|
567
|
+
document.addEventListener('click', (event) => {
|
|
568
|
+
if (modeMenu && !modeMenu.hidden && !event.target.closest('.ctrl-wrap')) closeModeMenu();
|
|
569
|
+
});
|
|
570
|
+
document.addEventListener('keydown', (event) => {
|
|
571
|
+
if (event.key === 'Escape') closeModeMenu();
|
|
572
|
+
});
|
|
573
|
+
modelPill?.addEventListener('click', () => openSettingsModal());
|
|
574
|
+
yesInput?.addEventListener('change', renderModePill);
|
|
575
|
+
|
|
507
576
|
function updateStatus(extra = {}) {
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
$('#sb-provider').textContent = p ? `${p.id}` : state.active.provider || '—';
|
|
511
|
-
$('#sb-model').textContent = state.active.model || '—';
|
|
577
|
+
renderModePill();
|
|
578
|
+
renderModelPill();
|
|
512
579
|
if (extra.rounds !== undefined) $('#sb-rounds').textContent = `${extra.rounds} rondas`;
|
|
513
580
|
if (extra.stopReason) $('#sb-rounds').textContent = extra.stopReason;
|
|
514
581
|
if (extra.running) $('#sb-rounds').textContent = 'pensando…';
|
|
@@ -1814,8 +1881,9 @@ function activateSession(id) {
|
|
|
1814
1881
|
|
|
1815
1882
|
function updateSubmitLabel(working) {
|
|
1816
1883
|
if (!submitBtn) return;
|
|
1817
|
-
submitBtn.
|
|
1818
|
-
submitBtn.
|
|
1884
|
+
submitBtn.dataset.working = working ? '1' : '0';
|
|
1885
|
+
submitBtn.setAttribute('aria-label', working ? 'encolar' : 'enviar');
|
|
1886
|
+
submitBtn.title = working ? 'El agente está trabajando: se envía al terminar' : 'enviar';
|
|
1819
1887
|
}
|
|
1820
1888
|
|
|
1821
1889
|
function auditStatus(record) {
|
|
@@ -5419,10 +5487,45 @@ function applyState(diff) {
|
|
|
5419
5487
|
|
|
5420
5488
|
/* --- señales de alerta: que se note aun mirando otra cosa ----------------- */
|
|
5421
5489
|
|
|
5422
|
-
const FAVICON_IDLE =
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5490
|
+
const FAVICON_IDLE = '/favicon.ico';
|
|
5491
|
+
const FAVICON_SOURCE = '/icons/icon-192x192.png';
|
|
5492
|
+
let faviconAlert = '';
|
|
5493
|
+
let faviconAlertPending = null;
|
|
5494
|
+
|
|
5495
|
+
/**
|
|
5496
|
+
* El favicon de alerta se dibuja sobre una copia del logo (mismo origen), para
|
|
5497
|
+
* que el punto rojo combine con el icono real y no con un SVG desincronizado.
|
|
5498
|
+
*/
|
|
5499
|
+
function alertFavicon() {
|
|
5500
|
+
if (faviconAlert) return Promise.resolve(faviconAlert);
|
|
5501
|
+
if (faviconAlertPending) return faviconAlertPending;
|
|
5502
|
+
faviconAlertPending = new Promise((resolve) => {
|
|
5503
|
+
const img = new Image();
|
|
5504
|
+
img.onload = () => {
|
|
5505
|
+
try {
|
|
5506
|
+
const canvas = document.createElement('canvas');
|
|
5507
|
+
canvas.width = 64;
|
|
5508
|
+
canvas.height = 64;
|
|
5509
|
+
const ctx = canvas.getContext('2d');
|
|
5510
|
+
ctx.drawImage(img, 0, 0, 64, 64);
|
|
5511
|
+
ctx.beginPath();
|
|
5512
|
+
ctx.arc(64 - 16, 16, 13, 0, Math.PI * 2);
|
|
5513
|
+
ctx.fillStyle = '#d9704a';
|
|
5514
|
+
ctx.fill();
|
|
5515
|
+
ctx.lineWidth = 3;
|
|
5516
|
+
ctx.strokeStyle = 'rgba(35,26,23,0.9)';
|
|
5517
|
+
ctx.stroke();
|
|
5518
|
+
faviconAlert = canvas.toDataURL('image/png');
|
|
5519
|
+
resolve(faviconAlert);
|
|
5520
|
+
} catch {
|
|
5521
|
+
resolve(FAVICON_IDLE);
|
|
5522
|
+
}
|
|
5523
|
+
};
|
|
5524
|
+
img.onerror = () => resolve(FAVICON_IDLE);
|
|
5525
|
+
img.src = FAVICON_SOURCE;
|
|
5526
|
+
});
|
|
5527
|
+
return faviconAlertPending;
|
|
5528
|
+
}
|
|
5426
5529
|
|
|
5427
5530
|
let baseTitle = '';
|
|
5428
5531
|
|
|
@@ -5437,7 +5540,10 @@ function renderAlertSignals() {
|
|
|
5437
5540
|
const clean = baseTitle.replace(/^\(⚠ \d+\) /, '');
|
|
5438
5541
|
document.title = pending > 0 ? `(⚠ ${pending}) ${clean}` : clean;
|
|
5439
5542
|
const link = document.querySelector('link[rel="icon"]');
|
|
5440
|
-
if (link)
|
|
5543
|
+
if (link) {
|
|
5544
|
+
if (pending > 0) alertFavicon().then((href) => { link.href = href; });
|
|
5545
|
+
else link.href = FAVICON_IDLE;
|
|
5546
|
+
}
|
|
5441
5547
|
}
|
|
5442
5548
|
|
|
5443
5549
|
/* --- canal global: solo latencia ------------------------------------------ */
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
6
|
<title>Tamnora Console</title>
|
|
7
|
-
<link rel="icon" href="
|
|
7
|
+
<link rel="icon" href="/favicon.ico" sizes="any" />
|
|
8
|
+
<link rel="apple-touch-icon" href="/icons/icon-192x192.png" />
|
|
9
|
+
<link rel="manifest" href="/manifest.webmanifest" />
|
|
10
|
+
<meta name="theme-color" content="#231a17" />
|
|
8
11
|
<script>
|
|
9
12
|
(function () {
|
|
10
13
|
var theme = null;
|
|
@@ -34,7 +37,7 @@
|
|
|
34
37
|
</head>
|
|
35
38
|
<body>
|
|
36
39
|
<header class="topbar">
|
|
37
|
-
<div class="brand"><span class="logo"><
|
|
40
|
+
<div class="brand"><span class="logo"><img src="/icons/logo.png" alt="Tamnora" /></span> <span class="ver">v0.9.6</span></div>
|
|
38
41
|
<nav class="tabs">
|
|
39
42
|
<button class="tab active" data-tab="chat">chat</button>
|
|
40
43
|
<button class="tab" data-tab="providers">proveedores</button>
|
|
@@ -151,16 +154,45 @@
|
|
|
151
154
|
<div id="attach-preview" class="attach-preview" hidden></div>
|
|
152
155
|
<div id="chat-queue" class="chat-queue" hidden></div>
|
|
153
156
|
<div id="audio-player" class="audio-player" hidden></div>
|
|
154
|
-
<div class="composer-
|
|
155
|
-
<
|
|
156
|
-
<button type="button" id="attach-image" class="btn icon" title="Adjuntar imagen (o pegala / arrastrala)" aria-label="adjuntar imagen">
|
|
157
|
+
<div class="composer-top">
|
|
158
|
+
<button type="button" id="cwd-pill" class="ctrl-pill" title="carpeta de trabajo de la sesión">
|
|
157
159
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
158
|
-
<
|
|
159
|
-
<circle cx="9" cy="9" r="2" />
|
|
160
|
-
<path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" />
|
|
160
|
+
<path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" />
|
|
161
161
|
</svg>
|
|
162
|
+
<span id="cwd-pill-label">sin carpeta</span>
|
|
163
|
+
<svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>
|
|
164
|
+
</button>
|
|
165
|
+
<div class="ctrl-wrap">
|
|
166
|
+
<button type="button" id="mode-pill" class="ctrl-pill" aria-haspopup="menu" aria-expanded="false" title="permisos del agente">
|
|
167
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" /></svg>
|
|
168
|
+
<span id="mode-pill-label">Workspace Write</span>
|
|
169
|
+
<svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>
|
|
170
|
+
</button>
|
|
171
|
+
<div id="mode-menu" class="ctrl-menu" role="menu" hidden>
|
|
172
|
+
<button type="button" class="ctrl-menu-item" data-kind="read" role="menuitemradio" aria-checked="false">
|
|
173
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z" /><circle cx="12" cy="12" r="3" /></svg>
|
|
174
|
+
<span>Read Only</span>
|
|
175
|
+
<svg class="ctrl-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg>
|
|
176
|
+
</button>
|
|
177
|
+
<button type="button" class="ctrl-menu-item" data-kind="write" role="menuitemradio" aria-checked="false">
|
|
178
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" /><path d="m15 5 4 4" /></svg>
|
|
179
|
+
<span>Workspace Write</span>
|
|
180
|
+
<svg class="ctrl-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg>
|
|
181
|
+
</button>
|
|
182
|
+
<button type="button" class="ctrl-menu-item" data-kind="full" role="menuitemradio" aria-checked="false">
|
|
183
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" /><path d="m9 12 2 2 4-4" /></svg>
|
|
184
|
+
<span>Full access</span>
|
|
185
|
+
<svg class="ctrl-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg>
|
|
186
|
+
</button>
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
<div class="composer-row">
|
|
192
|
+
<button type="button" id="attach-image" class="add-btn" title="Adjuntar imagen (o pegala / arrastrala)" aria-label="adjuntar imagen">
|
|
193
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14" /><path d="M12 5v14" /></svg>
|
|
162
194
|
</button>
|
|
163
|
-
<button type="button" id="mic-btn" class="btn
|
|
195
|
+
<button type="button" id="mic-btn" class="add-btn" title="Dictar por voz (tocá para grabar y parar)" aria-label="dictar por voz">
|
|
164
196
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
165
197
|
<path d="M12 19v3" />
|
|
166
198
|
<path d="M19 10v2a7 7 0 0 1-14 0v-2" />
|
|
@@ -170,27 +202,30 @@
|
|
|
170
202
|
</button>
|
|
171
203
|
<input id="image-input" type="file" accept="image/*" multiple hidden />
|
|
172
204
|
<textarea id="chat-input" rows="1" placeholder="escribí un mensaje…" title="Enter envía · Shift+Enter salto" spellcheck="false"></textarea>
|
|
173
|
-
<button class="
|
|
174
|
-
|
|
205
|
+
<button type="button" id="model-pill" class="ctrl-pill model-pill" title="proveedor y modelo activos">
|
|
206
|
+
<span id="model-pill-label">—</span>
|
|
207
|
+
<svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>
|
|
208
|
+
</button>
|
|
209
|
+
<button class="send-btn" type="submit" aria-label="enviar" title="enviar">
|
|
210
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m5 12 7-7 7 7" /><path d="M12 19V5" /></svg>
|
|
211
|
+
</button>
|
|
212
|
+
<button class="add-btn" type="button" id="stop-btn" title="Detener la respuesta" aria-label="detener" hidden>
|
|
213
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="6" y="6" width="12" height="12" rx="2" /></svg>
|
|
214
|
+
</button>
|
|
215
|
+
</div>
|
|
216
|
+
|
|
217
|
+
<div id="statusbar" class="composer-status" title="estado del run" role="button" tabindex="0">
|
|
218
|
+
<span id="sb-activity" class="sb-activity" hidden></span>
|
|
219
|
+
<span id="sb-sync" class="sb-channel retrying" hidden title="sin sincronía con el server"></span>
|
|
220
|
+
<span id="sb-channel" class="sb-channel" hidden title="canal de eventos entre dispositivos"></span>
|
|
221
|
+
<span id="sb-rounds">listo</span>
|
|
222
|
+
<span class="spacer"></span>
|
|
223
|
+
<span id="sb-context" class="ctx muted" title="uso del contexto del modelo">
|
|
224
|
+
<span id="sb-tokens">tokens —</span>
|
|
225
|
+
<span class="ctx-bar" aria-hidden="true"><span id="sb-context-fill" class="ctx-fill"></span></span>
|
|
226
|
+
</span>
|
|
175
227
|
</div>
|
|
176
228
|
</form>
|
|
177
|
-
<div id="statusbar" class="statusbar" title="cambiar modo, proveedor y modelo" role="button" tabindex="0">
|
|
178
|
-
<span id="sb-mode">agent</span>
|
|
179
|
-
<span class="dot">·</span>
|
|
180
|
-
<span id="sb-provider">—</span>
|
|
181
|
-
<span class="dot">·</span>
|
|
182
|
-
<span id="sb-model">—</span>
|
|
183
|
-
<span class="dot">·</span>
|
|
184
|
-
<span id="sb-activity" class="sb-activity" hidden></span>
|
|
185
|
-
<span id="sb-sync" class="sb-channel retrying" hidden title="sin sincronía con el server"></span>
|
|
186
|
-
<span id="sb-channel" class="sb-channel" hidden title="canal de eventos entre dispositivos"></span>
|
|
187
|
-
<span id="sb-rounds">listo</span>
|
|
188
|
-
<span class="spacer"></span>
|
|
189
|
-
<span id="sb-context" class="ctx muted" title="uso del contexto del modelo">
|
|
190
|
-
<span id="sb-tokens">tokens —</span>
|
|
191
|
-
<span class="ctx-bar" aria-hidden="true"><span id="sb-context-fill" class="ctx-fill"></span></span>
|
|
192
|
-
</span>
|
|
193
|
-
</div>
|
|
194
229
|
</div>
|
|
195
230
|
|
|
196
231
|
<div id="resizer-explorer" class="resizer" title="ajustar" role="separator" aria-orientation="vertical"></div>
|
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
6
|
<title>Tamnora Harness</title>
|
|
7
|
+
<link rel="icon" href="/favicon.ico" sizes="any" />
|
|
8
|
+
<link rel="apple-touch-icon" href="/icons/icon-192x192.png" />
|
|
9
|
+
<link rel="manifest" href="/manifest.webmanifest" />
|
|
10
|
+
<meta name="theme-color" content="#231a17" />
|
|
7
11
|
<script>
|
|
8
12
|
(function () {
|
|
9
13
|
var theme = null;
|
|
@@ -129,13 +133,13 @@
|
|
|
129
133
|
border-radius: 11px;
|
|
130
134
|
display: grid;
|
|
131
135
|
place-items: center;
|
|
136
|
+
overflow: hidden;
|
|
132
137
|
background: var(--surface);
|
|
133
138
|
backdrop-filter: blur(10px);
|
|
134
|
-
font-family: Georgia, serif;
|
|
135
|
-
font-weight: 700;
|
|
136
|
-
font-size: 19px;
|
|
137
139
|
}
|
|
138
140
|
|
|
141
|
+
.logo-mark img { width: 100%; height: 100%; display: block; object-fit: cover; }
|
|
142
|
+
|
|
139
143
|
.logo-text strong {
|
|
140
144
|
display: block;
|
|
141
145
|
font-family: var(--serif);
|
|
@@ -420,7 +424,7 @@
|
|
|
420
424
|
<div class="page">
|
|
421
425
|
<header>
|
|
422
426
|
<a href="/landing" class="logo">
|
|
423
|
-
<div class="logo-mark"
|
|
427
|
+
<div class="logo-mark"><img src="/icons/logo.png" alt="Tamnora" /></div>
|
|
424
428
|
<div class="logo-text">
|
|
425
429
|
<strong>Tamnora Harness</strong>
|
|
426
430
|
<small>Harness de agentes IA</small>
|
|
Binary file
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Tamnora Harness",
|
|
3
|
+
"short_name": "Tamnora",
|
|
4
|
+
"description": "Consola web del harness local de agentes IA.",
|
|
5
|
+
"start_url": "/",
|
|
6
|
+
"scope": "/",
|
|
7
|
+
"display": "standalone",
|
|
8
|
+
"background_color": "#f5efe2",
|
|
9
|
+
"theme_color": "#231a17",
|
|
10
|
+
"icons": [
|
|
11
|
+
{ "src": "/icons/icon-72x72.png", "sizes": "72x72", "type": "image/png" },
|
|
12
|
+
{ "src": "/icons/icon-96x96.png", "sizes": "96x96", "type": "image/png" },
|
|
13
|
+
{ "src": "/icons/icon-128x128.png", "sizes": "128x128", "type": "image/png" },
|
|
14
|
+
{ "src": "/icons/icon-144x144.png", "sizes": "144x144", "type": "image/png" },
|
|
15
|
+
{ "src": "/icons/icon-152x152.png", "sizes": "152x152", "type": "image/png" },
|
|
16
|
+
{ "src": "/icons/icon-192x192.png", "sizes": "192x192", "type": "image/png", "purpose": "any maskable" },
|
|
17
|
+
{ "src": "/icons/icon-384x384.png", "sizes": "384x384", "type": "image/png" },
|
|
18
|
+
{ "src": "/icons/icon-512x512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" }
|
|
19
|
+
]
|
|
20
|
+
}
|