nio-cat 0.5.0 → 0.6.1

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/style.css CHANGED
@@ -314,7 +314,7 @@ body.board-mode .view-enter { animation: none; }
314
314
 
315
315
  @media (prefers-reduced-motion: no-preference) {
316
316
  .view-enter { animation: view-in .25s var(--ease); }
317
- @keyframes view-in { from { opacity: 0; transform: translateY(10px); } }
317
+ @keyframes view-in { from { opacity: 0; } }
318
318
  }
319
319
  @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation: none !important; transition: none !important; } }
320
320
 
@@ -348,12 +348,13 @@ body.board-mode .view-enter { animation: none; }
348
348
  .paste .pair-form { flex-direction: column; }
349
349
  .side nav .grp { padding: 16px 12px 4px; }
350
350
  .top h1 { font-size: 26px; }
351
- .chat { height: calc(100dvh - 56px - var(--safe-t)); }
352
- body.chat-mode { overflow: hidden; }
353
- body.chat-mode .shell { min-height: 0; }
354
- body.chat-mode .mbar { position: fixed; left: 0; right: 0; transform: translateY(var(--vvt, 0px)); }
355
- body.chat-mode .chat { position: fixed; left: 0; right: 0; top: calc(56px + var(--safe-t) + var(--vvt, 0px)); height: calc(var(--vvh, 100dvh) - 56px - var(--safe-t)); }
356
- body.chat-mode.kb .composer { padding-bottom: 10px; }
351
+ /* Al mòbil només fa scroll el document: res de contenidors amb scroll propi. */
352
+ .chat { height: auto; min-height: calc(100dvh - 56px - var(--safe-t)); }
353
+ .mode-bar { position: sticky; top: calc(56px + var(--safe-t)); z-index: 20; background: color-mix(in srgb, var(--bg) 82%, transparent); -webkit-backdrop-filter: blur(18px) saturate(160%); backdrop-filter: blur(18px) saturate(160%); }
354
+ .feed { flex: 1 0 auto; overflow: visible; overscroll-behavior: auto; }
355
+ .composer { position: sticky; bottom: var(--kb, 0px); z-index: 20; background: var(--bg); }
356
+ body.kb .composer { padding-bottom: 10px; }
357
+ .tool pre, .approval pre, .ev-logs pre, .proj-list { max-height: none; overflow-y: visible; }
357
358
  .composer textarea, input[type="text"], input[type="password"], input[type="url"], input[type="search"], select, textarea.field { font-size: 16px; }
358
359
  .chat-head { display: none; }
359
360
  .mode-bar { padding: 10px 16px; }
@@ -482,13 +483,14 @@ a.kbd { text-decoration: none; color: inherit; }
482
483
  /* Comandes amb «/» */
483
484
  .composer { position: relative; }
484
485
  .slash { position: absolute; left: 24px; right: 24px; bottom: 100%; max-width: 760px; margin: 0 auto 6px; max-height: min(320px, 45dvh); overflow-y: auto; overscroll-behavior: contain; display: grid; padding: 6px; border: 1px solid var(--border); border-radius: 14px; background: var(--white); box-shadow: 0 10px 30px rgba(39, 34, 36, .12); z-index: 5; }
485
- .slash button { display: grid; grid-template-columns: auto 1fr auto; gap: 4px 12px; align-items: baseline; text-align: left; padding: 9px 10px; border: 0; border-radius: 9px; background: none; }
486
+ .slash button { display: grid; grid-template-columns: minmax(0, auto) minmax(0, 1fr) auto; gap: 2px 12px; align-items: center; text-align: left; padding: 9px 10px; border: 0; border-radius: 9px; background: none; width: 100%; }
486
487
  .slash button[aria-selected="true"] { background: var(--bg-2); }
487
- .slash .n { font-weight: 600; }
488
+ .slash .n { font-weight: 600; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
488
489
  .slash .h { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--fg-muted); font-size: 13px; }
489
- .slash .t { font-size: 11px; padding: 1px 7px; border-radius: 6px; background: var(--bg-2); color: var(--fg-muted); }
490
+ .slash .t { display: inline-flex; align-items: center; justify-content: center; height: 20px; padding: 0 8px; border-radius: 10px; background: var(--bg-2); color: var(--fg-muted); font-size: 11px; font-weight: 600; line-height: 1; white-space: nowrap; }
491
+ .slash button[aria-selected="true"] .t { background: var(--white); }
490
492
  .slash .t.ag { background: color-mix(in srgb, var(--lemon) 60%, var(--bg)); color: var(--fg); }
491
- @media (max-width: 820px) { .slash { left: 12px; right: 12px; } .slash .h { grid-column: 1 / -1; grid-row: 2; } }
493
+ @media (max-width: 820px) { .slash { left: 12px; right: 12px; } .slash button { grid-template-columns: minmax(0, 1fr) auto; } .slash .h { grid-column: 1 / -1; grid-row: 2; } .slash .t { grid-column: 2; grid-row: 1; } }
492
494
  /* ---------- Veu ---------- */
493
495
  .ev-error b { font-size: 12px; letter-spacing: .02em; text-transform: uppercase; }
494
496
  .speak { display: inline-flex; align-items: center; gap: 6px; margin-top: 8px; height: 28px; padding: 0 10px; border: 1px solid var(--border); border-radius: 999px; background: none; color: var(--fg-muted); font-size: 12px; font-weight: 600; }
@@ -563,3 +565,63 @@ dialog.sheet.wiz.compact .sheet-in, .compact .wiz-body { flex: 0 1 auto; }
563
565
  .chip .agent-tag { gap: 7px; }
564
566
  .sheet .list + .or { margin-top: 18px; }
565
567
  .sheet #modelForm { margin-top: 4px; }
568
+
569
+ @media (max-width: 820px) {
570
+ .new-fab { position: fixed; right: 16px; bottom: calc(72px + var(--safe-b)); z-index: 25; height: 48px; padding: 0 18px 0 14px; border-radius: 16px; box-shadow: 0 14px 30px -14px color-mix(in srgb, var(--fg) 70%, transparent); }
571
+ body:not(.no-tabs):not(.kb) .main:has(.new-fab) { padding-bottom: calc(130px + var(--safe-b)); }
572
+ }
573
+
574
+ .skel { display: block; height: 14px; border-radius: 8px; background: linear-gradient(90deg, var(--border) 25%, color-mix(in srgb, var(--border) 45%, var(--white)) 50%, var(--border) 75%) 0 0 / 200% 100%; animation: skel 1.3s linear infinite; }
575
+ .skel.tall { height: 22px; }
576
+ .skel.r { margin-left: auto; }
577
+ .skel-card { display: grid; gap: 12px; padding: 18px; border-radius: var(--radius); border: 1px solid var(--border); background: var(--white); }
578
+ .skel-feed { display: grid; gap: 14px; padding: 8px 16px; max-width: 760px; margin: 0 auto; width: 100%; box-sizing: border-box; }
579
+ @keyframes skel { to { background-position: -200% 0; } }
580
+ .chip.fav { border-color: color-mix(in srgb, var(--lemon) 70%, var(--border)); }
581
+ .chip[data-fav][aria-pressed="true"] { background: var(--lemon); border-color: var(--lemon); }
582
+ .manage-bar { position: sticky; top: 0; z-index: 5; display: grid; gap: 10px; margin: 0 0 12px; padding: 12px; border: 1px solid var(--border); border-radius: 14px; background: var(--bg); }
583
+ .manage-bar .row { flex-wrap: wrap; }
584
+ .session-card.pick-mode { cursor: pointer; position: relative; padding-left: 46px; user-select: none; }
585
+ .session-card.pick-mode::before { content: ""; position: absolute; left: 14px; top: 16px; width: 20px; height: 20px; border-radius: 6px; border: 1.5px solid var(--border); background: var(--white); }
586
+ .session-card.picked { border-color: var(--fg); }
587
+ .session-card.picked::before { background: var(--fg); border-color: var(--fg); }
588
+ .session-card.picked::after { content: ""; position: absolute; left: 21px; top: 20px; width: 6px; height: 11px; border: solid var(--bg); border-width: 0 2px 2px 0; transform: rotate(45deg); }
589
+
590
+ .mode-bar .chip { height: 30px; gap: 7px; padding: 0 12px 0 10px; }
591
+ .mode-bar .chip > .ico, .mode-bar .chip > .agent-tag .ico { width: 15px; height: 15px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
592
+ .mode-bar .chip > span:not(.agent-tag) { line-height: 1; }
593
+ .mode-bar .chip .abot { --s: 18px; display: inline-grid; place-items: center; }
594
+ .mode-bar .chip .agent-tag { gap: 7px; line-height: 1; }
595
+
596
+ /* lliscar entre pàgines: es mou només el contingut; barres i botons fixos no es toquen */
597
+ @media (max-width: 820px) {
598
+ .main { overflow-x: clip; }
599
+ #view.swiping .page, #view.swipe-anim .page { position: relative; left: var(--sx, 0); opacity: var(--so, 1); will-change: left, opacity; }
600
+ #view.swipe-anim .page { transition: left .18s var(--ease), opacity .18s ease-out; }
601
+ }
602
+
603
+ /* ---------- Motiu i captura a les aprovacions ---------- */
604
+ .approval .why { display: grid; gap: 2px; padding: 10px 12px; border-radius: 8px; background: color-mix(in srgb, var(--lemon) 22%, var(--white)); }
605
+ .approval .why p { font-size: 14px; white-space: pre-wrap; overflow-wrap: anywhere; }
606
+ .approval .shot, .screen-img { width: 100%; border-radius: 10px; border: 1px solid var(--border); background: var(--bg-2); display: block; }
607
+
608
+ /* ---------- Activitat ---------- */
609
+ .act-filter { margin-bottom: 16px; overflow-x: auto; scrollbar-width: none; }
610
+ .act-filter button { padding: 0 12px; }
611
+ .act { display: grid; }
612
+ .act-day { font-size: 12px; font-weight: 600; color: var(--fg-muted); margin: 18px 0 6px; text-transform: capitalize; }
613
+ .act-day:first-child { margin-top: 0; }
614
+ .act-row { display: flex; gap: 12px; padding: 10px 0; border-top: 1px solid color-mix(in srgb, var(--border) 70%, transparent); }
615
+ .act-t { flex: none; width: 44px; font-size: 12px; color: var(--fg-muted); padding-top: 2px; }
616
+ .act-row .row { gap: 8px; }
617
+ .act-what { overflow-wrap: anywhere; color: var(--fg); margin-top: 2px; }
618
+ .act-row .verdict { font-size: 12px; }
619
+
620
+ /* ---------- Feines ---------- */
621
+ .job .title { font-size: 16px; font-weight: 500; letter-spacing: -.02em; margin-top: 8px; }
622
+ .job .meta { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 8px; font-size: 12px; color: var(--fg-muted); }
623
+ .chip.ok { color: var(--moss); border-color: color-mix(in srgb, var(--moss) 40%, transparent); }
624
+ .grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
625
+ .check { gap: 10px; font-size: 14px; }
626
+ .check input { width: 18px; height: 18px; accent-color: var(--fg); }
627
+ @media (max-width: 520px) { .grid-2 { grid-template-columns: 1fr; } }
package/web/board.css CHANGED
@@ -104,9 +104,10 @@
104
104
  .bd-chat .m.nio b { font-weight: 600; }
105
105
  .bd-chat .mav { --s: 24px; --bw: .15; margin-bottom: 4px; }
106
106
  @keyframes bd-msg { from { opacity: 0; transform: translateY(6px); } }
107
- .bd-chat .chips { display: flex; gap: 6px; overflow-x: auto; padding: 0 16px 10px; scrollbar-width: none; }
107
+ .bd-chat .chips { flex: none; display: flex; gap: 6px; overflow-x: auto; overflow-y: hidden; padding: 4px 16px 10px; scrollbar-width: none; -webkit-overflow-scrolling: touch; overscroll-behavior-x: contain; scroll-snap-type: x proximity; }
108
+ .bd-chat header, .bd-chat .brain, .bd-chat .cin { flex: none; }
108
109
  .bd-chat .chips::-webkit-scrollbar { display: none; }
109
- .bd-chat .chip-s { flex: none; max-width: 260px; box-sizing: border-box; height: 32px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 0 12px; line-height: 30px; text-align: center; border: 1px solid var(--border); border-radius: 999px; background: var(--white); font-size: 12px; font-weight: 500; cursor: pointer; }
110
+ .bd-chat .chip-s { flex: none; scroll-snap-align: start; max-width: 260px; box-sizing: border-box; height: 32px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 0 12px; line-height: 30px; text-align: center; border: 1px solid var(--border); border-radius: 999px; background: var(--white); font-size: 12px; font-weight: 500; cursor: pointer; }
110
111
  .bd-chat .chip-s:hover { border-color: var(--fg); }
111
112
  .bd-chat .cin { display: flex; gap: 8px; align-items: flex-end; padding: 12px 14px calc(14px + env(safe-area-inset-bottom)); border-top: 1px solid var(--border); }
112
113
  .bd-chat .cin textarea { flex: 1; min-height: 42px; max-height: 140px; padding: 10px 12px; border: 1px solid var(--border); border-radius: 12px; background: var(--white); resize: none; font: inherit; font-size: 14px; line-height: 1.4; outline: none; }
package/web/board.js CHANGED
@@ -952,7 +952,8 @@ function drawBrains() {
952
952
  const sel_ = R.root.querySelector('#bdBrain'); if (!sel_) return;
953
953
  const list = window.NioBrains?.list() || [{ id: 'rules', label: 'Ordres ràpides' }];
954
954
  const cur = list.some((b) => b.id === B.opts.brain) ? B.opts.brain : 'rules'; // la preferència es manté encara que ara no hi sigui
955
- sel_.innerHTML = list.map((b) => `<option value="${esc(b.id)}" ${b.id === cur ? 'selected' : ''}>${esc(b.label)}</option>`).join('');
955
+ const html = list.map((b) => `<option value="${esc(b.id)}" ${b.id === cur ? 'selected' : ''}>${esc(b.label)}</option>`).join('');
956
+ if (sel_.dataset.sig !== html) { sel_.dataset.sig = html; sel_.innerHTML = html; }
956
957
  }
957
958
 
958
959
  /* ---------- Panell d'estil ---------- */
@@ -1034,7 +1035,7 @@ function openChat(open, focus = true, persist = true) {
1034
1035
  R.root.classList.toggle('chat-open', open);
1035
1036
  R.chat.inert = !open;
1036
1037
  const t = R.root.querySelector('[data-bd=chat]'); t.setAttribute('aria-pressed', open);
1037
- if (open && focus) setTimeout(() => R.input.focus(), reduce ? 0 : 280);
1038
+ if (open && focus && !matchMedia('(pointer: coarse)').matches) setTimeout(() => R.input.focus(), reduce ? 0 : 280);
1038
1039
  if (!open && wasOpen && R.chat.contains(document.activeElement)) t.focus({ preventScroll: true });
1039
1040
  }
1040
1041
 
@@ -1175,7 +1176,7 @@ function mount(root) {
1175
1176
  on(R.input, 'keydown', e => { if (e.key === 'Enter' && !e.shiftKey && !e.isComposing) { e.preventDefault(); send(); } if (e.key === 'Escape') { e.stopPropagation(); openChat(false); } });
1176
1177
  on(R.input, 'input', () => { R.input.style.height = ''; R.input.style.height = Math.min(140, R.input.scrollHeight) + 'px'; });
1177
1178
  on(R.root.querySelector('#bdBrain'), 'change', (e) => { B.opts.brain = e.target.value; save(); say(B.opts.brain === 'rules' ? 'Faig servir les ordres ràpides. Escriu <b>ajuda</b> per veure-les.' : `Ara t’entendré amb <b>${esc(e.target.selectedOptions[0].textContent)}</b>. Demana’m el diagrama com vulguis.`); });
1178
- on(R.root.querySelector('#bdBrain'), 'focus', drawBrains);
1179
+ on(R.root.querySelector('#bdBrain'), 'focus', () => { drawBrains(); window.NioBrains?.refresh?.(); });
1179
1180
  on(R.root.querySelector('#bdImport'), 'change', e => { const f = e.target.files[0]; if (f) importJSON(f); e.target.value = ''; });
1180
1181
  on(document, 'click', e => { if (R && !e.target.closest('.bd-menuwrap')) { const m = R.root.querySelector('#bdMenu'); if (m) m.hidden = true; } });
1181
1182
  on(window, 'resize', () => { applyView(); drawSel(); });
package/web/sw.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // nio · service worker: obre l'app encara que no hi hagi connexió i mostra els avisos.
2
- const SHELL = 'nio-shell-v14';
2
+ const SHELL = 'nio-shell-v20';
3
3
  const FILES = ['/app', '/app/style.css', '/app/app.js', '/bot.css', '/bot.js', '/board.css', '/board.js', '/favicon.svg', '/manifest.webmanifest'];
4
4
 
5
5
  self.addEventListener('install', (e) => {
@@ -37,16 +37,20 @@ self.addEventListener('message', (e) => {
37
37
  }
38
38
  });
39
39
 
40
- // Els avisos arriben buits: el detall es demana directament a l'ordinador.
40
+ // Normalment l'avís arriba xifrat de punta a punta amb el text dins (RFC 8291).
41
+ // Si arriba buit (nio antic), en demanem el detall a l'ordinador.
41
42
  self.addEventListener('push', (e) => {
42
43
  e.waitUntil((async () => {
43
- let title = 'nio', body = 'Un agent et necessita', session = null;
44
- try {
45
- const cfg = await (await (await caches.open('nio-cfg')).match('/cfg')).json();
46
- const [n] = await (await fetch(`${cfg.url}/api/notices`, { headers: { Authorization: `Bearer ${cfg.token}` } })).json();
47
- if (n) { title = n.title; body = n.body; session = n.session; }
48
- } catch { /* sense detall: avís genèric */ }
49
- await self.registration.showNotification(title, { body, icon: '/icon-192.png', badge: '/icon-192.png', tag: session || 'nio', renotify: true, data: { session } });
44
+ let n = null;
45
+ try { n = e.data ? e.data.json() : null; } catch { n = null; }
46
+ if (!n) {
47
+ try {
48
+ const cfg = await (await (await caches.open('nio-cfg')).match('/cfg')).json();
49
+ [n] = await (await fetch(`${cfg.url}/api/notices`, { headers: { Authorization: `Bearer ${cfg.token}` } })).json();
50
+ } catch { /* sense detall: avís genèric */ }
51
+ }
52
+ const title = n?.title || 'nio', body = n?.body || 'Un agent et necessita', session = n?.session || null;
53
+ await self.registration.showNotification(title, { body, icon: '/icon-192.png', badge: '/badge.png', tag: session || n?.job || 'nio', renotify: true, data: { session, job: n?.job || null } });
50
54
  })());
51
55
  });
52
56