estela 0.1.39 → 0.1.40

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/web/app.js CHANGED
@@ -428,7 +428,14 @@ function haceCuanto(days) {
428
428
 
429
429
  async function loadSummary() {
430
430
  const { from, to } = periodRange(state.period === "all" ? "quarter" : state.period);
431
- state.summary = await api(`/api/summary?from=${from}&to=${to}`);
431
+ // La cuota es siempre de esta semana, sea cual sea el periodo elegido: si
432
+ // fallara, el resumen se pinta igual y la tarjeta simplemente no sale.
433
+ const [summary, quota] = await Promise.all([
434
+ api(`/api/summary?from=${from}&to=${to}`),
435
+ api("/api/quota").catch(() => null),
436
+ ]);
437
+ state.summary = summary;
438
+ state.quota = quota;
432
439
  renderSummary();
433
440
  }
434
441
 
@@ -473,6 +480,8 @@ function renderSummary() {
473
480
  parts.push(`<div class="plist">${s.projects.map(renderProjectRow).join("")}</div>`);
474
481
  }
475
482
 
483
+ parts.push(renderQuota(state.quota));
484
+
476
485
  if (s.activity.length) {
477
486
  parts.push(`<h2 class="block-title">${tr("summary.activity")}</h2>`);
478
487
  parts.push(`<div class="feed">${s.activity.map(renderActivity).join("")}</div>`);
@@ -510,6 +519,57 @@ function renderSummary() {
510
519
  });
511
520
  }
512
521
 
522
+ /* ¿Qué proyecto se come tu cuota? El mismo reparto de la semana que da
523
+ `estela quota`, por agente y nunca mezclados: Claude Code gasta la cuota de
524
+ Anthropic y Codex la de OpenAI. Solo consumo real: ni "te queda" ni
525
+ "se acaba", porque esos límites no se publican. */
526
+ function renderQuota(q) {
527
+ if (!q || (!q.agents.length && !q.excluded.length)) return "";
528
+ const AGENT = { "claude-code": "Claude Code", codex: "Codex" };
529
+ const out = [];
530
+
531
+ for (const a of q.agents) {
532
+ out.push(`<h2 class="block-title">${tr("quota.title", { agent: AGENT[a.agent] || a.agent })}</h2>`);
533
+ out.push(`<div class="plist">${a.week.projects.map(quotaRow).join("")}</div>`);
534
+ out.push(`<p class="quota-note">${esc(quotaComparison(a.comparison))}</p>`);
535
+ }
536
+ if (q.excluded.length) out.push(`<p class="quota-note">${tr("quota.copilot")}</p>`);
537
+ if (q.agents.length) out.push(`<p class="quota-note">${tr("quota.estimate")}</p>`);
538
+ return out.join("");
539
+ }
540
+
541
+ function quotaRow(p) {
542
+ const name = p.projectName ?? (p.folder ? tr("quota.folder", { folder: p.folder }) : tr("quota.noFolder"));
543
+ const pct = Math.round(p.share * 100);
544
+ return `
545
+ <div class="prow">
546
+ <div class="prow-text">
547
+ <div class="prow-name">${esc(name)}</div>
548
+ <div class="prow-meta">${tr("quota.breakdown", {
549
+ input: fmtTokens(p.input), output: fmtTokens(p.output),
550
+ read: fmtTokens(p.cacheRead), write: fmtTokens(p.cacheWrite) })}</div>
551
+ <div class="quota-bar"><i style="width:${pct}%"></i></div>
552
+ </div>
553
+ <div class="prow-figs">
554
+ <div class="prow-hours">${fmtTokens(p.total)}</div>
555
+ <div class="prow-amount">${tr("quota.share", { n: pct })}</div>
556
+ </div>
557
+ </div>`;
558
+ }
559
+
560
+ function quotaComparison(c) {
561
+ if (c.status !== "ok") return tr("quota.noHistory", { weeks: c.weeks, have: c.completeWeeks });
562
+ if (c.ratio === null) return tr("quota.noPastUsage", { current: fmtTokens(c.current), weeks: c.weeks });
563
+ const diff = Math.round((c.ratio - 1) * 100);
564
+ const how = diff > 0 ? tr("quota.more", { n: diff }) : diff < 0 ? tr("quota.less", { n: -diff }) : tr("quota.same");
565
+ return tr("quota.vsHistory", { current: fmtTokens(c.current), average: fmtTokens(c.average), weeks: c.weeks, how });
566
+ }
567
+
568
+ /** Tokens con el formato corto del idioma: "22,7 M" o "22.7M". */
569
+ function fmtTokens(n) {
570
+ return new Intl.NumberFormat(localeTag(), { notation: "compact", maximumFractionDigits: 1 }).format(n);
571
+ }
572
+
513
573
  function kpi(value, label, extra = "") {
514
574
  return `<div class="kpi ${extra}">
515
575
  <div class="kpi-value">${esc(value)}</div>
package/web/i18n.js CHANGED
@@ -40,6 +40,7 @@
40
40
  "refresh.searching": "Buscando…",
41
41
  "refresh.failed": "No se pudo actualizar",
42
42
  "upgrade.btn": "Actualizar a Pro o Teams",
43
+ "upgrade.btnShort": "Pro o Teams",
43
44
  "upgrade.cycle": "Ciclo de facturación",
44
45
  "upgrade.monthly": "Mensual",
45
46
  "upgrade.yearly": "Anual",
@@ -140,6 +141,19 @@
140
141
  "summary.approveAll": "Hay <b>{n}</b> bloques de trabajo <b>tuyo</b> sin revisar en este periodo. Al configurar un proyecto entra de golpe todo su histórico. Pulsa el aviso de cada proyecto para ir a ellos.",
141
142
  "summary.approveAll.btn": "Aprobar el periodo",
142
143
  "summary.activity": "Actividad reciente",
144
+ "quota.title": "Cuota de {agent} esta semana",
145
+ "quota.breakdown": "entrada {input} · salida {output} · lect. caché {read} · escr. caché {write}",
146
+ "quota.share": "{n} % de la semana",
147
+ "quota.folder": "sin proyecto · {folder}",
148
+ "quota.noFolder": "sin carpeta",
149
+ "quota.vsHistory": "Frente a tu historial: {current} esta semana, y una media de {average} en el mismo tramo de las {weeks} semanas anteriores: {how}.",
150
+ "quota.noPastUsage": "Frente a tu historial: {current} esta semana; en el mismo tramo de las {weeks} semanas anteriores no hubo consumo.",
151
+ "quota.noHistory": "Sin historial suficiente para comparar: hacen falta {weeks} semanas completas y hay {have}.",
152
+ "quota.more": "un {n} % más",
153
+ "quota.less": "un {n} % menos",
154
+ "quota.same": "lo mismo",
155
+ "quota.copilot": "Copilot no guarda tokens fiables y queda fuera de este cálculo.",
156
+ "quota.estimate": "Solo consumo real, calculado en tu máquina: los límites de cada plan no se publican, y lo que uses en claude.ai u otro equipo también cuenta y Estela no lo ve. El detalle de la ventana de 5 h, con <code>estela quota</code>.",
143
157
  "summary.nothingLeft": "Ya no queda nada por revisar ahí",
144
158
  "summary.pendingHere": "{blocks} por revisar aquí · {more}",
145
159
  "summary.moreDays": { one: "1 día más con pendientes", other: "{n} días más con pendientes" },
@@ -323,6 +337,7 @@
323
337
  "refresh.searching": "Looking…",
324
338
  "refresh.failed": "Couldn't refresh",
325
339
  "upgrade.btn": "Upgrade to Pro or Teams",
340
+ "upgrade.btnShort": "Pro or Teams",
326
341
  "upgrade.cycle": "Billing cycle",
327
342
  "upgrade.monthly": "Monthly",
328
343
  "upgrade.yearly": "Yearly",
@@ -423,6 +438,19 @@
423
438
  "summary.approveAll": "There are <b>{n}</b> blocks of <b>your</b> work unreviewed in this period. Setting up a project brings in its whole history at once. Click each project's notice to go to them.",
424
439
  "summary.approveAll.btn": "Approve the period",
425
440
  "summary.activity": "Recent activity",
441
+ "quota.title": "{agent} quota this week",
442
+ "quota.breakdown": "input {input} · output {output} · cache read {read} · cache write {write}",
443
+ "quota.share": "{n}% of the week",
444
+ "quota.folder": "no project · {folder}",
445
+ "quota.noFolder": "no folder",
446
+ "quota.vsHistory": "Against your history: {current} this week, and an average of {average} over the same stretch of the previous {weeks} weeks: {how}.",
447
+ "quota.noPastUsage": "Against your history: {current} this week; over the same stretch of the previous {weeks} weeks there was no usage.",
448
+ "quota.noHistory": "Not enough history to compare: it takes {weeks} complete weeks and there are {have}.",
449
+ "quota.more": "{n}% more",
450
+ "quota.less": "{n}% less",
451
+ "quota.same": "about the same",
452
+ "quota.copilot": "Copilot doesn't record reliable tokens and is left out of this calculation.",
453
+ "quota.estimate": "Real usage only, calculated on your machine: each plan's limits aren't published, and anything you use on claude.ai or another machine also counts and Estela can't see it. For the 5-hour window, run <code>estela quota</code>.",
426
454
  "summary.nothingLeft": "Nothing left to review there",
427
455
  "summary.pendingHere": "{blocks} to review here · {more}",
428
456
  "summary.moreDays": { one: "1 more day with pending blocks", other: "{n} more days with pending blocks" },
package/web/index.html CHANGED
@@ -30,7 +30,10 @@
30
30
  data-i18n="sync.now" data-i18n-title="sync.now.title"
31
31
  title="Sube tus horas a la nube y baja lo de tus otras máquinas">Sincronizar</button>
32
32
  <div class="upgrade-wrap" id="upgrade-wrap" hidden>
33
- <button type="button" class="btn-main btn-upgrade" id="upgrade-btn" data-i18n="upgrade.btn">Actualizar a Pro o Teams</button>
33
+ <button type="button" class="btn-main btn-upgrade" id="upgrade-btn"
34
+ data-i18n-aria-label="upgrade.btn" aria-label="Actualizar a Pro o Teams"><span class="up-long"
35
+ data-i18n="upgrade.btn">Actualizar a Pro o Teams</span><span class="up-short"
36
+ data-i18n="upgrade.btnShort">Pro o Teams</span></button>
34
37
  <div class="upgrade-menu" id="upgrade-menu" hidden>
35
38
  <div class="upgrade-ciclo" role="group" aria-label="Ciclo de facturación" data-i18n-aria-label="upgrade.cycle">
36
39
  <button type="button" data-ciclo="monthly" class="is-on" data-i18n="upgrade.monthly">Mensual</button>