nio-cat 0.9.2 → 0.9.3
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/bin/nio.js +31 -21
- package/package.json +1 -1
- package/web/bot.css +2 -3
- package/web/index.html +148 -126
- package/web/og.png +0 -0
- package/web/privadesa.html +1 -1
- package/web/sw.js +1 -1
package/bin/nio.js
CHANGED
|
@@ -46,12 +46,14 @@ const c = {
|
|
|
46
46
|
warn: (s) => `\x1b[33m${s}\x1b[0m`,
|
|
47
47
|
};
|
|
48
48
|
|
|
49
|
+
const row = (label, value, note) => console.log(` ${c.dim(label.padEnd(10))} ${value}${note ? c.dim(' ' + note) : ''}`);
|
|
50
|
+
|
|
49
51
|
const cfg = config.load();
|
|
50
52
|
const auth = new Auth(undefined, cfg);
|
|
51
53
|
|
|
52
54
|
async function doctor() {
|
|
53
55
|
const agents = await detectAll(cfg, true);
|
|
54
|
-
console.log(c.b('\n Agents
|
|
56
|
+
console.log(c.b('\n Agents\n'));
|
|
55
57
|
for (const a of agents) {
|
|
56
58
|
const mark = a.available ? c.ok('✓') : c.dim('·');
|
|
57
59
|
const v = a.available ? c.dim(a.version || '') : c.dim(`no hi és · ${a.login}`);
|
|
@@ -93,8 +95,8 @@ function openDashboard() {
|
|
|
93
95
|
const online = !flags.local;
|
|
94
96
|
// &qr=1: el panell mostra el QR per al mòbil tot just obrir-se, a la mateixa pestanya.
|
|
95
97
|
const link = pairLink(online ? flags.app || cfg.app : local, local, cfg.token) + '&qr=1';
|
|
96
|
-
if (canOpenBrowser() && openUrl(link)) { panelOpened = true;
|
|
97
|
-
else
|
|
98
|
+
if (canOpenBrowser() && openUrl(link)) { panelOpened = true; row('Panell', online ? (flags.app || cfg.app) + '/app' : local, '(obert al navegador)'); }
|
|
99
|
+
else row('Panell', link);
|
|
98
100
|
}
|
|
99
101
|
|
|
100
102
|
async function showPair(publicUrl) {
|
|
@@ -110,18 +112,26 @@ async function showPair(publicUrl) {
|
|
|
110
112
|
const link = pairLink(app, publicUrl, secret), code = makeCode(publicUrl, secret), qrLink = codeLink(app, code);
|
|
111
113
|
const file = writePairPage(link, code, qrLink);
|
|
112
114
|
// El QR ha de cabre al terminal: marge normal, marge d'1 mòdul i, si no hi cap, la pàgina del navegador.
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
console.log(
|
|
118
|
-
console.log(
|
|
115
|
+
if (panelOpened) {
|
|
116
|
+
console.log(`\n ${c.b('Connecta el mòbil')}`);
|
|
117
|
+
console.log(` Al panell del navegador prem ${c.b('«Continua al mòbil»')} i escaneja el codi.`);
|
|
118
|
+
console.log(c.dim(' També pots enganxar aquest codi a Sessions → Tria un ordinador:\n'));
|
|
119
|
+
console.log(` ${code}\n`);
|
|
120
|
+
console.log(c.dim(' Serveix per a un sol dispositiu i caduca en 15 minuts. Per a un altre: nio pair.\n'));
|
|
119
121
|
} else {
|
|
120
|
-
|
|
122
|
+
const rows = process.stdout.rows || 999, cols = process.stdout.columns || 999;
|
|
123
|
+
const fits = (m) => { const z = terminalQrSize(qrLink, m); return z.rows + 9 <= rows && z.cols + 2 <= cols; };
|
|
124
|
+
const margin = fits(2) ? 2 : fits(1) ? 1 : 0;
|
|
125
|
+
console.log(`\n ${c.b('Connecta el mòbil')}\n`);
|
|
126
|
+
if (margin) {
|
|
127
|
+
console.log(` Escaneja aquest codi amb la càmera:\n`);
|
|
128
|
+
console.log(terminalQr(qrLink, { margin }));
|
|
129
|
+
console.log('');
|
|
130
|
+
}
|
|
131
|
+
console.log(` ${margin ? 'O copia' : 'Copia'} aquest codi i enganxa'l a Sessions → Tria un ordinador:\n`);
|
|
132
|
+
console.log(` ${code}\n`);
|
|
133
|
+
console.log(c.dim(' Serveix per a un sol dispositiu i caduca en 15 minuts. Per a un altre: nio pair.\n'));
|
|
121
134
|
}
|
|
122
|
-
console.log(`\n ${c.b('Codi:')} ${code}`);
|
|
123
|
-
console.log(c.dim(' Copia\'l i enganxa\'l a Sessions → Tria un ordinador.'));
|
|
124
|
-
console.log(c.dim(' Serveix per a un sol dispositiu i caduca en 15 minuts. Per a un altre: nio pair.\n'));
|
|
125
135
|
if (flags.open) {
|
|
126
136
|
const opener = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
127
137
|
try {
|
|
@@ -168,7 +178,7 @@ function registerMemory(force) {
|
|
|
168
178
|
if (which(cfg.agents?.codex?.bin || 'codex')) {
|
|
169
179
|
try { out.push(`Codex ${mcp.registerCodex()}`); } catch (e) { out.push(`Codex: ${e.message}`); }
|
|
170
180
|
}
|
|
171
|
-
|
|
181
|
+
row('Memòria', memory.DIR, out.length ? `(${out.join(' · ')})` : '');
|
|
172
182
|
}
|
|
173
183
|
|
|
174
184
|
async function start() {
|
|
@@ -181,7 +191,7 @@ async function start() {
|
|
|
181
191
|
|
|
182
192
|
console.log(`\n ${c.b('nio')} ${c.dim('· agents d’IA al teu ordinador, des del mòbil')}`);
|
|
183
193
|
await doctor();
|
|
184
|
-
|
|
194
|
+
row('Carpetes', cfg.roots.join(', '));
|
|
185
195
|
|
|
186
196
|
// Sense cloudflared no hi ha túnel: en lloc de quedar-nos sense codi QR, connectem per la wifi.
|
|
187
197
|
if (!flags.lan && !flags.url && !flags['no-tunnel'] && !which('cloudflared')) {
|
|
@@ -215,7 +225,7 @@ async function start() {
|
|
|
215
225
|
const tg = new Telegram({ cfg, hub, audit, available: jobs.available });
|
|
216
226
|
tg.hookUrl = jobs.hookUrl;
|
|
217
227
|
tg.start();
|
|
218
|
-
if (cfg.telegram?.token)
|
|
228
|
+
if (cfg.telegram?.token) row('Telegram', 'actiu', `(${(cfg.telegram.allowed || []).length} xats autoritzats)`);
|
|
219
229
|
const control = new Control({ cfg, hub, audit });
|
|
220
230
|
const remote = new Remote({ cfg, audit });
|
|
221
231
|
const routes = [...memory.routes(), ...jobs.routes(), ...voice.routes(cfg), ...tg.routes(), ...control.routes(), ...remote.routes()];
|
|
@@ -228,14 +238,14 @@ async function start() {
|
|
|
228
238
|
});
|
|
229
239
|
|
|
230
240
|
server.listen(cfg.port, host, () => {
|
|
231
|
-
|
|
241
|
+
row('Local', `http://127.0.0.1:${cfg.port}`);
|
|
232
242
|
openDashboard();
|
|
233
243
|
|
|
234
244
|
if (flags.lan) {
|
|
235
245
|
const ip = lanIps()[0];
|
|
236
246
|
if (ip) {
|
|
237
247
|
publicUrl = publicUrl || `http://${ip}:${cfg.port}`;
|
|
238
|
-
|
|
248
|
+
row('Xarxa', publicUrl, '(només la mateixa wifi)');
|
|
239
249
|
}
|
|
240
250
|
}
|
|
241
251
|
|
|
@@ -251,7 +261,7 @@ async function start() {
|
|
|
251
261
|
const named = cfg.tunnel && cfg.tunnel.mode === 'named' && !flags.quick && !flags.url && !flags['no-tunnel'] && !flags.lan ? cfg.tunnel : null;
|
|
252
262
|
if (named) {
|
|
253
263
|
const t = startConfiguredTunnel(named, cfg.port, {
|
|
254
|
-
onReady: () =>
|
|
264
|
+
onReady: () => row('Túnel', `https://${named.host}`, `${c.ok('connectat')} (adreça fixa)`),
|
|
255
265
|
onDown: (code, wait) => console.log(c.warn(` El túnel s’ha tallat (codi ${code}). El torno a obrir d’aquí a ${Math.round(wait / 1000)} s.`)),
|
|
256
266
|
});
|
|
257
267
|
if (t) {
|
|
@@ -264,7 +274,7 @@ async function start() {
|
|
|
264
274
|
if (fixed) {
|
|
265
275
|
const nt = startNamedTunnel(cfg.tunnelToken, () => {
|
|
266
276
|
publicUrl = cfg.fixedUrl;
|
|
267
|
-
|
|
277
|
+
row('Adreça', publicUrl, '(fixa)');
|
|
268
278
|
cfg.lastUrl = publicUrl;
|
|
269
279
|
config.save(cfg);
|
|
270
280
|
showPair(publicUrl);
|
|
@@ -274,7 +284,7 @@ async function start() {
|
|
|
274
284
|
|
|
275
285
|
const t = startTunnel(cfg.port, (url) => {
|
|
276
286
|
publicUrl = url;
|
|
277
|
-
|
|
287
|
+
row('Túnel', url, '(Cloudflare, sense cost)');
|
|
278
288
|
cfg.lastUrl = url;
|
|
279
289
|
config.save(cfg);
|
|
280
290
|
showPair(url);
|
package/package.json
CHANGED
package/web/bot.css
CHANGED
|
@@ -51,9 +51,8 @@
|
|
|
51
51
|
|
|
52
52
|
/* Com a O del nom */
|
|
53
53
|
.word { display: inline-flex; align-items: baseline; }
|
|
54
|
-
/*
|
|
55
|
-
|
|
56
|
-
.word .bot { --s: .56em; width: .547em; height: .578em; border-width: .099em .111em; margin-left: .031em; top: .016em; }
|
|
54
|
+
/* Un cercle ple, una mica més gran que la «o» de Manrope perquè tingui cos i s'equilibri amb «ni». */
|
|
55
|
+
.word .bot { --s: .64em; --bw: .17; width: .64em; height: .64em; border-width: .108em; margin-left: .05em; top: .018em; }
|
|
57
56
|
|
|
58
57
|
@media (prefers-reduced-motion: reduce) {
|
|
59
58
|
.bot, .bot * { animation: none !important; transition: none !important; }
|
package/web/index.html
CHANGED
|
@@ -31,77 +31,137 @@
|
|
|
31
31
|
<link rel="stylesheet" href="/bot.css">
|
|
32
32
|
<style>
|
|
33
33
|
:root {
|
|
34
|
-
--bg: #f0ede7;
|
|
35
|
-
--
|
|
36
|
-
--fg-muted: #69615c;
|
|
37
|
-
--accent: #f46a3b;
|
|
38
|
-
--border: #cec7bd;
|
|
39
|
-
--lemon: #e7e66a;
|
|
40
|
-
--red: #e2382b;
|
|
34
|
+
--bg: #f0ede7; --fg: #272224; --fg-muted: #69615c; --accent: #f46a3b; --border: #cec7bd; --lemon: #e7e66a; --red: #e2382b;
|
|
35
|
+
--peach: #f9d9cc; --blue: #d9dff5; --green: #d5e6d0; --lilac: #e8d9ee; --teal: #d3e7ea; --rose: #f3d0d0;
|
|
41
36
|
--font: "Manrope", system-ui, sans-serif;
|
|
37
|
+
--ease: cubic-bezier(.22, 1, .36, 1);
|
|
42
38
|
}
|
|
43
39
|
* { box-sizing: border-box; margin: 0; padding: 0; -webkit-tap-highlight-color: transparent; }
|
|
44
|
-
html { -webkit-text-size-adjust: 100%; }
|
|
45
|
-
body { background: var(--bg); color: var(--fg); font: 400
|
|
40
|
+
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
|
|
41
|
+
body { background: var(--bg); color: var(--fg); font: 400 18px/1.6 var(--font); letter-spacing: -.02em; -webkit-font-smoothing: antialiased; }
|
|
46
42
|
a { color: inherit; }
|
|
47
43
|
::selection { background: var(--lemon); }
|
|
48
|
-
:focus-visible { outline: 2px solid var(--fg); outline-offset: 3px; border-radius:
|
|
49
|
-
|
|
50
|
-
.
|
|
51
|
-
.
|
|
52
|
-
.head .enter { display: inline-flex; align-items: center; gap: 8px; height:
|
|
44
|
+
:focus-visible { outline: 2px solid var(--fg); outline-offset: 3px; border-radius: 4px; }
|
|
45
|
+
.page { width: min(1080px, 100% - 64px); margin: 0 auto; }
|
|
46
|
+
.head { padding: 40px 0 0; display: flex; justify-content: space-between; align-items: center; }
|
|
47
|
+
.mark { font-size: 30px; font-weight: 600; letter-spacing: -.07em; line-height: 1; text-decoration: none; }
|
|
48
|
+
.head .enter { display: inline-flex; align-items: center; gap: 8px; height: 38px; padding: 0 16px 0 14px; border: 1px solid var(--border); border-radius: 999px; font-size: 14px; font-weight: 500; text-decoration: none; transition: border-color .2s, background-color .2s; }
|
|
53
49
|
.head .enter svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
|
|
54
50
|
.head .enter:hover { border-color: var(--fg); background: color-mix(in srgb, var(--fg) 4%, transparent); }
|
|
55
|
-
.mark { font-size: 28px; font-weight: 600; letter-spacing: -.07em; line-height: 1; text-decoration: none; }
|
|
56
51
|
|
|
57
|
-
|
|
58
|
-
h1 { font-size: clamp(
|
|
59
|
-
.lead { margin-top:
|
|
52
|
+
.hero { padding: 88px 0 24px; max-width: 760px; }
|
|
53
|
+
h1 { font-size: clamp(38px, 6.4vw, 68px); font-weight: 500; line-height: 1.04; letter-spacing: -.058em; text-wrap: balance; }
|
|
54
|
+
.lead { margin-top: 24px; max-width: 34em; font-size: clamp(18px, 2.2vw, 21px); line-height: 1.5; color: var(--fg-muted); text-wrap: pretty; }
|
|
55
|
+
.install { margin-top: 36px; display: flex; align-items: center; gap: 6px; max-width: 560px; padding: 7px 7px 7px 22px; border-radius: 16px; background: var(--fg); color: var(--bg); }
|
|
56
|
+
.install code { background: none; border-radius: 0; flex: 1; min-width: 0; overflow-x: auto; white-space: nowrap; padding: 9px 0; font: 500 15.5px ui-monospace, "SF Mono", Menlo, monospace; scrollbar-width: none; }
|
|
57
|
+
.install code::-webkit-scrollbar { display: none; }
|
|
58
|
+
.hero .alt { margin-top: 14px; font-size: 15px; color: var(--fg-muted); }
|
|
59
|
+
.copy { flex: none; display: inline-grid; place-items: center; width: 42px; height: 42px; border: 0; border-radius: 11px; background: rgba(255,255,255,.1); color: inherit; cursor: pointer; transition: background-color .2s, transform .12s; }
|
|
60
|
+
.copy:hover { background: rgba(255,255,255,.2); }
|
|
61
|
+
.copy:active { transform: scale(.95); }
|
|
62
|
+
.copy svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
|
|
63
|
+
.copy .ok { display: none; }
|
|
64
|
+
.copy.done .ok { display: block; animation: copy-ok .2s var(--ease); }
|
|
65
|
+
.copy.done .ic { display: none; }
|
|
66
|
+
@keyframes copy-ok { from { opacity: 0; transform: scale(.6); } }
|
|
60
67
|
|
|
61
|
-
section { margin-top:
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
68
|
+
section { margin-top: 96px; scroll-margin-top: 24px; }
|
|
69
|
+
.sec-head { max-width: 640px; margin-bottom: 32px; }
|
|
70
|
+
.eyebrow { display: inline-block; margin-bottom: 12px; font-size: 13px; font-weight: 600; letter-spacing: -.01em; color: var(--fg-muted); }
|
|
71
|
+
h2 { font-size: clamp(28px, 4vw, 42px); font-weight: 500; line-height: 1.1; letter-spacing: -.05em; text-wrap: balance; }
|
|
72
|
+
.sec-head p { margin-top: 14px; color: var(--fg-muted); text-wrap: pretty; }
|
|
65
73
|
main a { text-decoration-thickness: 1px; text-underline-offset: 4px; text-decoration-color: var(--accent); }
|
|
66
74
|
main a:hover { color: var(--accent); }
|
|
67
|
-
main ul.list { padding-left: 1.2em; display: grid; gap: .5em; }
|
|
68
|
-
main code { font: 500 .88em ui-monospace, "SF Mono", Menlo, monospace; padding: 2px 7px; border-radius: 7px; background: #e6e1d8; }
|
|
69
|
-
main .cmd { display: flex; align-items: center; gap: 4px; padding: 6px 6px 6px 18px; border-radius: 12px; background: #e6e1d8; }
|
|
70
|
-
main .cmd code { flex: 1; min-width: 0; overflow-x: auto; white-space: nowrap; padding: 8px 0; background: none; border-radius: 0; font-size: 16px; scrollbar-width: none; }
|
|
71
|
-
main .cmd code::-webkit-scrollbar { display: none; }
|
|
72
|
-
.copy { flex: none; display: inline-grid; place-items: center; width: 38px; height: 38px; padding: 0; border: 0; border-radius: 9px; background: none; color: var(--fg-muted); cursor: pointer; transition: color .2s, background-color .2s, transform .12s; }
|
|
73
|
-
.copy:hover { color: var(--fg); }
|
|
74
|
-
.copy:active { transform: scale(.97); }
|
|
75
|
-
.copy svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
|
|
76
|
-
.copy .ok { display: none; }
|
|
77
|
-
.copy.done .ok { display: block; color: #5b6551; animation: copy-ok .2s cubic-bezier(.22,1,.36,1); }
|
|
78
|
-
@keyframes copy-ok { from { opacity: 0; transform: scale(.6); } }
|
|
79
|
-
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation: none; transition: none; } }
|
|
80
|
-
.copy.done .ic { display: none; }
|
|
81
75
|
main strong { font-weight: 600; }
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
.
|
|
85
|
-
.
|
|
86
|
-
.
|
|
87
|
-
.
|
|
88
|
-
.
|
|
89
|
-
|
|
76
|
+
code { font: 500 .86em ui-monospace, "SF Mono", Menlo, monospace; padding: 2px 7px; border-radius: 7px; background: rgba(255,255,255,.62); }
|
|
77
|
+
|
|
78
|
+
.steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; counter-reset: s; }
|
|
79
|
+
.step { position: relative; padding: 28px; border-radius: 22px; counter-increment: s; display: flex; flex-direction: column; gap: 10px; }
|
|
80
|
+
.step::before { content: counter(s); display: grid; place-items: center; width: 34px; height: 34px; border-radius: 50%; background: var(--fg); color: var(--bg); font-size: 15px; font-weight: 600; margin-bottom: 8px; }
|
|
81
|
+
.step h3 { font-size: 22px; font-weight: 500; letter-spacing: -.04em; line-height: 1.15; }
|
|
82
|
+
.step p { font-size: 16px; line-height: 1.55; text-wrap: pretty; }
|
|
83
|
+
.c-peach { background: var(--peach); } .c-blue { background: var(--blue); } .c-green { background: var(--green); } .c-lilac { background: var(--lilac); } .c-teal { background: var(--teal); } .c-rose { background: var(--rose); } .c-lemon { background: var(--lemon); }
|
|
84
|
+
|
|
85
|
+
.grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
|
|
86
|
+
.card { padding: 24px; border-radius: 22px; display: flex; flex-direction: column; gap: 8px; transition: transform .3s var(--ease); }
|
|
87
|
+
.card:hover { transform: translateY(-3px); }
|
|
88
|
+
.card .tag { align-self: flex-start; padding: 3px 10px; border-radius: 999px; background: rgba(255,255,255,.6); font-size: 12px; font-weight: 600; letter-spacing: -.01em; }
|
|
89
|
+
.card h3 { margin-top: 8px; font-size: 20px; font-weight: 500; letter-spacing: -.04em; line-height: 1.2; }
|
|
90
|
+
.card p { font-size: 15.5px; line-height: 1.55; text-wrap: pretty; color: color-mix(in srgb, var(--fg) 82%, transparent); }
|
|
91
|
+
|
|
92
|
+
.feature { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: stretch; }
|
|
93
|
+
.panel { padding: 32px; border-radius: 26px; display: flex; flex-direction: column; gap: 14px; }
|
|
94
|
+
.panel h3 { font-size: 26px; font-weight: 500; letter-spacing: -.045em; line-height: 1.12; text-wrap: balance; }
|
|
95
|
+
.panel p { text-wrap: pretty; line-height: 1.55; }
|
|
96
|
+
.panel .muted { font-size: 15.5px; color: color-mix(in srgb, var(--fg) 75%, transparent); }
|
|
97
|
+
.rows { display: grid; margin-top: 4px; }
|
|
98
|
+
.rows > div { display: grid; grid-template-columns: minmax(0, 12.5rem) minmax(0, 1fr); gap: 4px 16px; align-items: baseline; padding: 11px 0; border-top: 1px solid color-mix(in srgb, var(--fg) 14%, transparent); }
|
|
99
|
+
.rows dt code { white-space: nowrap; }
|
|
100
|
+
.rows dd { font-size: 15px; line-height: 1.45; color: color-mix(in srgb, var(--fg) 78%, transparent); }
|
|
101
|
+
|
|
102
|
+
.own { padding: 44px; border-radius: 28px; background: var(--fg); color: var(--bg); display: grid; grid-template-columns: 1.1fr 1fr; gap: 32px; align-items: center; }
|
|
103
|
+
.own h2 { color: var(--bg); }
|
|
104
|
+
.own p { color: color-mix(in srgb, var(--bg) 78%, transparent); text-wrap: pretty; }
|
|
105
|
+
.own a { color: var(--bg); text-decoration-color: var(--accent); }
|
|
106
|
+
.own .btns { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 20px; }
|
|
107
|
+
.btn { display: inline-flex; align-items: center; height: 44px; padding: 0 22px; border-radius: 999px; background: var(--bg); color: var(--fg); font-weight: 600; font-size: 15px; text-decoration: none; }
|
|
108
|
+
.btn.ghost { background: none; color: var(--bg); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--bg) 35%, transparent); }
|
|
109
|
+
main .own a.btn:hover { color: var(--fg); background: var(--lemon); }
|
|
90
110
|
|
|
91
111
|
|
|
92
|
-
.
|
|
112
|
+
.c-lemonsoft { background: #efeeb0; }
|
|
113
|
+
.card.big { grid-column: 1 / -1; display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1.05fr); gap: 40px; padding: 36px; border-radius: 26px; align-items: start; }
|
|
114
|
+
.card.big:hover { transform: none; }
|
|
115
|
+
.card.big h3 { font-size: 28px; letter-spacing: -.045em; line-height: 1.12; text-wrap: balance; margin-bottom: 6px; }
|
|
116
|
+
.big-l { display: flex; flex-direction: column; gap: 14px; }
|
|
117
|
+
.big-l p { font-size: 16px; line-height: 1.6; text-wrap: pretty; }
|
|
118
|
+
.big-l .muted { font-size: 15px; color: color-mix(in srgb, var(--fg) 68%, transparent); }
|
|
119
|
+
.card code { background: #e6e2da; color: var(--fg); }
|
|
120
|
+
.card .rows { margin-top: 10px; }
|
|
121
|
+
.card .rows > div { grid-template-columns: minmax(0, 11.5rem) minmax(0, 1fr); gap: 4px 16px; padding: 8px 0; align-items: center; }
|
|
122
|
+
.card .rows dt code { display: inline-block; padding: 4px 9px; }
|
|
123
|
+
.card .rows dd { font-size: 14px; line-height: 1.35; }
|
|
124
|
+
.card .rows dt code { font-size: .82em; }
|
|
125
|
+
.card.big .rows > div { grid-template-columns: 1fr; gap: 6px; }
|
|
126
|
+
.card .rows.stack > div { grid-template-columns: 1fr !important; gap: 6px; }
|
|
127
|
+
.card .note { font-size: 13.5px; color: color-mix(in srgb, var(--fg) 65%, transparent); margin-top: 6px; }
|
|
128
|
+
.card:not(.big) .rows > div { grid-template-columns: minmax(0, 8rem) minmax(0, 1fr); }
|
|
129
|
+
.own a.btn { color: var(--fg); }
|
|
130
|
+
.own a.btn.ghost { color: var(--bg); }
|
|
131
|
+
|
|
132
|
+
.footer .page { padding: 64px 0 48px; text-align: center; font-size: 13px; color: var(--fg-muted); }
|
|
93
133
|
.footer a { color: var(--fg); text-decoration: underline; text-decoration-color: var(--red); text-decoration-thickness: 1px; text-underline-offset: 3px; }
|
|
94
134
|
.footer a:hover { color: var(--red); }
|
|
135
|
+
main { padding-bottom: 8px; }
|
|
136
|
+
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation: none !important; transition: none !important; scroll-behavior: auto !important; } }
|
|
95
137
|
|
|
138
|
+
@media (max-width: 920px) {
|
|
139
|
+
.grid { grid-template-columns: repeat(2, 1fr); }
|
|
140
|
+
.steps { grid-template-columns: 1fr; }
|
|
141
|
+
.step { padding: 24px; }
|
|
142
|
+
.card.big { grid-template-columns: 1fr; gap: 24px; padding: 28px; }
|
|
143
|
+
.card .rows.two { grid-template-columns: 1fr; }
|
|
144
|
+
.own { grid-template-columns: 1fr; padding: 32px; }
|
|
145
|
+
}
|
|
96
146
|
@media (max-width: 620px) {
|
|
97
147
|
body { font-size: 17px; }
|
|
98
148
|
.page { width: calc(100% - 40px); }
|
|
99
|
-
.head { padding-top:
|
|
100
|
-
|
|
101
|
-
.
|
|
102
|
-
section { margin-top:
|
|
103
|
-
|
|
104
|
-
|
|
149
|
+
.head { padding-top: 24px; }
|
|
150
|
+
.mark { font-size: 28px; }
|
|
151
|
+
.hero { padding-top: 56px; }
|
|
152
|
+
section { margin-top: 72px; }
|
|
153
|
+
.sec-head { margin-bottom: 24px; }
|
|
154
|
+
.grid { grid-template-columns: 1fr; gap: 12px; }
|
|
155
|
+
.card { padding: 22px; }
|
|
156
|
+
.card.big { padding: 24px; border-radius: 22px; }
|
|
157
|
+
.card.big h3 { font-size: 24px; }
|
|
158
|
+
.card .rows > div, .card:not(.big) .rows > div { grid-template-columns: 1fr; gap: 6px; }
|
|
159
|
+
.panel { padding: 24px; border-radius: 22px; }
|
|
160
|
+
.panel h3 { font-size: 23px; }
|
|
161
|
+
.rows > div { grid-template-columns: 1fr; gap: 2px; }
|
|
162
|
+
.install { padding-left: 16px; }
|
|
163
|
+
.install code { font-size: 13.5px; }
|
|
164
|
+
.own { padding: 28px 24px; border-radius: 22px; }
|
|
105
165
|
}
|
|
106
166
|
</style>
|
|
107
167
|
</head>
|
|
@@ -112,93 +172,55 @@ main strong { font-weight: 600; }
|
|
|
112
172
|
</header>
|
|
113
173
|
|
|
114
174
|
<main class="page">
|
|
115
|
-
<
|
|
116
|
-
|
|
175
|
+
<div class="hero">
|
|
176
|
+
<h1>Els teus agents, a les teves màquines, des del mòbil.</h1>
|
|
177
|
+
<p class="lead">nio és un comandament per a Claude Code, Codex i altres agents. Treballen als teus ordinadors, amb els teus comptes i les teves claus. Tu els dones feina des del mòbil, els segueixes en directe i aproves el que és arriscat. Personal i gratuït.</p>
|
|
178
|
+
<div class="install"><code id="cmd">curl -fsSL https://www.nio.cat/install.sh | sh</code><button class="copy" type="button" data-copy="curl -fsSL https://www.nio.cat/install.sh | sh" aria-label="Copia l’ordre"><svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><rect x="8" y="8" width="12" height="12" rx="2"/><path d="M16 8V5a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h3"/></svg><svg class="ok" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg></button></div>
|
|
179
|
+
<p class="alt">Linux o Mac. Per provar-ho sense instal·lar: <code>npx nio-cat</code></p>
|
|
180
|
+
</div>
|
|
117
181
|
|
|
118
182
|
<section aria-labelledby="com">
|
|
119
|
-
<h2 id="com">
|
|
120
|
-
<
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
183
|
+
<div class="sec-head"><span class="eyebrow">Com funciona</span><h2 id="com">Tres passos i ja estàs dins</h2></div>
|
|
184
|
+
<div class="steps">
|
|
185
|
+
<div class="step c-peach"><h3>Instal·la’l a l’ordinador</h3><p>Una sola ordre. Instal·la nio, el deixa engegat i t’obre la web per enllaçar el mòbil.</p></div>
|
|
186
|
+
<div class="step c-blue"><h3>Escaneja’l amb el mòbil</h3><p>Obre <a href="/app">nio.cat/app</a> al mòbil i escaneja el codi QR que et mostra la web. El mòbil rep una clau pròpia, que pots revocar quan vulguis. Afegeix-lo a la pantalla d’inici i tindràs avisos.</p></div>
|
|
187
|
+
<div class="step c-green"><h3>Dona-li feina</h3><p>Tria l’ordinador, l’agent i la carpeta, i explica què vols. Si l’ordinador es reinicia, el mòbil el torna a trobar sol.</p></div>
|
|
188
|
+
</div>
|
|
125
189
|
</section>
|
|
126
190
|
|
|
127
191
|
<section aria-labelledby="que">
|
|
128
|
-
<
|
|
129
|
-
<
|
|
130
|
-
<
|
|
131
|
-
<
|
|
132
|
-
<
|
|
133
|
-
<
|
|
134
|
-
<
|
|
135
|
-
<
|
|
136
|
-
<
|
|
137
|
-
<
|
|
138
|
-
<
|
|
139
|
-
<
|
|
140
|
-
<
|
|
141
|
-
<
|
|
142
|
-
|
|
143
|
-
</ul>
|
|
144
|
-
</section>
|
|
145
|
-
|
|
146
|
-
<section aria-labelledby="cmds">
|
|
147
|
-
<h2 id="cmds">Comandes amb «/»</h2>
|
|
148
|
-
<p>Al missatge d’una sessió, escriu <code>/</code> i surten els suggeriments. Les de nio funcionen amb tots els agents; les de l’agent (comandes i skills) se li envien tal qual.</p>
|
|
149
|
-
<dl class="cmds">
|
|
150
|
-
<div><dt><code>/mode</code></dt><dd>Canvia els permisos: <code>l</code> llegir, <code>p</code> preguntar, <code>t</code> tot</dd></div>
|
|
151
|
-
<div><dt><code>/model</code></dt><dd>Canvia de model</dd></div>
|
|
152
|
-
<div><dt><code>/stop</code></dt><dd>Atura l’agent ara mateix</dd></div>
|
|
153
|
-
<div><dt><code>/nova</code></dt><dd>Nova sessió amb el mateix agent i carpeta</dd></div>
|
|
154
|
-
<div><dt><code>/carpeta</code></dt><dd>Canvia la carpeta de la sessió</dd></div>
|
|
155
|
-
<div><dt><code>/git</code></dt><dd>Veure canvis, fer commit i crear un PR</dd></div>
|
|
156
|
-
<div><dt><code>/nom</code></dt><dd>Posa nom a la sessió</dd></div>
|
|
157
|
-
<div><dt><code>/opcions</code></dt><dd>Opcions de la sessió</dd></div>
|
|
158
|
-
<div><dt><code>/ajuda</code></dt><dd>Llista de comandes</dd></div>
|
|
159
|
-
</dl>
|
|
160
|
-
</section>
|
|
161
|
-
|
|
162
|
-
<section aria-labelledby="screen">
|
|
163
|
-
<h2 id="screen">La pantalla remota</h2>
|
|
164
|
-
<p>Obre l’escriptori d’un dels teus ordinadors a pantalla completa, a la mateixa amplada que el mòbil, com un escriptori remot. Al mode <strong>Mirar</strong> només veus què fa l’agent; al mode <strong>Controlar</strong> pots clicar, fer doble clic, clic dret, arrossegar, desplaçar i escriure.</p>
|
|
165
|
-
<dl class="cmds">
|
|
166
|
-
<div><dt><code>− 100% +</code></dt><dd>Allunya, apropa o torna a enquadrar la imatge</dd></div>
|
|
167
|
-
<div><dt><code>1:1</code></dt><dd>Mida real, per llegir text petit</dd></div>
|
|
168
|
-
<div><dt><code>Desa captura</code></dt><dd>Baixa el fotograma actual com a imatge</dd></div>
|
|
169
|
-
<div><dt><code>Pantalla completa</code></dt><dd>A l’ordinador, amaga tota la resta</dd></div>
|
|
170
|
-
</dl>
|
|
171
|
-
<p><strong>Zoom amb dos dits.</strong> Pessiga per apropar i arrossega amb dos dits per moure’t; al mode Mirar, doble toc alterna entre enquadrar i apropar. Al mode Controlar, el botó «2 dits» tria si els dos dits desplacen l’ordinador o fan zoom. A l’ordinador, pessiga al trackpad o fes servir <code>Ctrl</code> i la roda. Cal activar «Apps de l’escriptori» a l’ordinador, i al Mac donar al terminal els permisos de Gravació de pantalla i Accessibilitat.</p>
|
|
172
|
-
</section>
|
|
173
|
-
|
|
174
|
-
<section aria-labelledby="board">
|
|
175
|
-
<h2 id="board">La pissarra</h2>
|
|
176
|
-
<p>Un llenç infinit per fer fluxos, mapes mentals i organigrames, al mòbil i a l’ordinador. Rectangles, el·lipses, rombes, fletxes que s’enganxen a les figures, línies, llapis, text i goma. Desfés i refés, quadrícula, i exportació a PNG, SVG o JSON.</p>
|
|
177
|
-
<p><strong>Zoom amb dos dits.</strong> Al mòbil, pessiga sobre el llenç per apropar o allunyar, i mou-lo amb els dos dits alhora. A l’ordinador, pessiga al trackpad o fes servir <code>Ctrl</code> i la roda del ratolí.</p>
|
|
178
|
-
<p><strong>Demana-ho a en Nio.</strong> Obre el xat de la pissarra i escriu què vols:</p>
|
|
179
|
-
<dl class="cmds">
|
|
180
|
-
<div><dt><code>Flux: A -> B -> C</code></dt><dd>Una fletxa per línia</dd></div>
|
|
181
|
-
<div><dt><code>Mapa mental de X: a, b, c</code></dt><dd>Tema central amb branques</dd></div>
|
|
182
|
-
<div><dt><code>Organigrama: Direcció > Finances</code></dt><dd>Qui depèn de qui</dd></div>
|
|
183
|
-
<div><dt><code>Connecta Banc amb ERP</code></dt><dd>Uneix dues figures</dd></div>
|
|
184
|
-
<div><dt><code>Pinta Banc de verd</code></dt><dd>Canvia el color</dd></div>
|
|
185
|
-
<div><dt><code>Plantilla d’aprovació</code></dt><dd>Un diagrama de partida</dd></div>
|
|
186
|
-
</dl>
|
|
192
|
+
<div class="sec-head"><span class="eyebrow">Què hi pots fer</span><h2 id="que">Tot el que necessites per treballar amb agents</h2></div>
|
|
193
|
+
<div class="grid">
|
|
194
|
+
<article class="card c-blue"><span class="tag">Agents</span><h3>Sessions en directe</h3><p>Claude Code, Codex, Grok i Antigravity a qualsevol dels teus ordinadors. Tries la carpeta i els permisos, i la resposta arriba mentre treballa.</p></article>
|
|
195
|
+
<article class="card c-rose"><span class="tag">Seguretat</span><h3>Aprovacions</h3><p>Abans d’editar, executar o clicar res, te’n demana permís amb el motiu. Ho aproves o ho rebutges amb un toc, i tot queda registrat.</p></article>
|
|
196
|
+
<article class="card c-lilac"><span class="tag">Connexió</span><h3>Avisos al mòbil</h3><p>Una notificació quan un agent et demana permís o acaba. Arriba buida: el detall el demana el mòbil directament a l’ordinador.</p></article>
|
|
197
|
+
<article class="card c-blue"><span class="tag">Agents</span><h3>Feines</h3><p>Que es facin soles: ara, més tard o cada dia, i encadenades («quan Claude Code acabi, que Codex ho revisi»).</p></article>
|
|
198
|
+
<article class="card c-blue"><span class="tag">Agents</span><h3>Memòria compartida</h3><p>El que recorda un agent, ho sap l’altre. Són fitxers markdown al teu ordinador.</p></article>
|
|
199
|
+
<article class="card c-teal"><span class="tag">Ordinador</span><h3>Ordinadors i servidors</h3><p>El teu ordinador o un servidor (VPS), tots a la mateixa llista. Els afegeixes escrivint <code>usuari@servidor</code>.</p></article>
|
|
200
|
+
<article class="card c-blue"><span class="tag">Agents</span><h3>Comandes amb «/»</h3><p>Escriu «/» i surten els suggeriments: <code>/mode</code>, <code>/model</code>, <code>/stop</code>, <code>/nova</code>, <code>/git</code>… Les de nio funcionen amb tots els agents; les de l’agent (comandes i skills) se li envien tal qual.</p></article>
|
|
201
|
+
<article class="card c-peach"><span class="tag">Pantalla remota</span><h3>L’escriptori, a la butxaca</h3><p>Obre l’escriptori de l’ordinador des del mòbil. Amb <strong>Mirar</strong> només veus què fa l’agent; amb <strong>Controlar</strong> hi cliques, arrossegues i escrius. Pessiga amb dos dits per fer zoom. Els agents també hi poden mirar webs i apps, sempre amb permís.</p></article>
|
|
202
|
+
<article class="card c-lemonsoft"><span class="tag">Pissarra</span><h3>Pensar dibuixant</h3><p>Un llenç infinit per a fluxos, mapes mentals i organigrames, amb figures, fletxes i llapis. Si li expliques a en Nio què vols, te’ls dibuixa. Exporta a PNG, SVG o JSON.</p></article>
|
|
203
|
+
<article class="card c-blue"><span class="tag">Agents</span><h3>Git i GitHub</h3><p>Clona un repositori, mira els canvis, fes commit i crea un PR des del mòbil, amb el <code>git</code> i el <code>gh</code> de l’ordinador.</p></article>
|
|
204
|
+
<article class="card c-lilac"><span class="tag">Connexió</span><h3>Telegram i veu</h3><p>Parla amb el teu bot o amb notes de veu, aprova amb un botó i escolta les respostes. Les claus es queden a l’ordinador.</p></article>
|
|
205
|
+
<article class="card c-green"><span class="tag">Xat local</span><h3>Ollama i xat local</h3><p>Fes servir models gratuïts a l’ordinador, o un model petit dins del navegador del mòbil, sense connexió.</p></article>
|
|
206
|
+
</div>
|
|
187
207
|
</section>
|
|
188
208
|
|
|
189
209
|
<section aria-labelledby="teu">
|
|
190
|
-
<
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
210
|
+
<div class="own">
|
|
211
|
+
<div>
|
|
212
|
+
<h2 id="teu">Tot és teu</h2>
|
|
213
|
+
</div>
|
|
214
|
+
<div>
|
|
215
|
+
<p>Fas servir els teus agents i les teves claus (Anthropic, OpenAI, ElevenLabs, el bot de Telegram). Les converses, la memòria i les claus es queden als teus ordinadors. nio.cat només serveix l’app i guarda, signada, l’adreça on trobar cada ordinador. Més detalls a <a href="/privadesa">privadesa</a>.</p>
|
|
216
|
+
<div class="btns"><a class="btn" href="/app">Obre el panell</a><a class="btn ghost" href="/qui-soc">Qui sóc</a><a class="btn ghost" href="/novetats">Novetats</a></div>
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
197
219
|
</section>
|
|
198
220
|
</main>
|
|
199
221
|
|
|
200
222
|
<footer class="footer">
|
|
201
|
-
<div class="page">© 2026 nio // un projecte de <a href="https://f.cat">f.cat</a> · <a href="mailto:hola@nio.cat">hola@nio.cat</a> · <a href="/novetats">Novetats</a> · <a href="/privadesa">Privadesa</a></div>
|
|
223
|
+
<div class="page">© 2026 nio // un projecte de <a href="https://f.cat">f.cat</a> · <a href="mailto:hola@nio.cat">hola@nio.cat</a> · <a href="/qui-soc">Qui sóc</a> · <a href="/novetats">Novetats</a> · <a href="/privadesa">Privadesa</a></div>
|
|
202
224
|
</footer>
|
|
203
225
|
<script src="/bot.js" defer></script>
|
|
204
226
|
<script>
|
package/web/og.png
CHANGED
|
Binary file
|
package/web/privadesa.html
CHANGED
|
@@ -141,7 +141,7 @@ main strong { font-weight: 600; }
|
|
|
141
141
|
</main>
|
|
142
142
|
|
|
143
143
|
<footer class="footer">
|
|
144
|
-
<div class="page">© 2026 nio // un projecte de <a href="https://f.cat">f.cat</a> · <a href="mailto:hola@nio.cat">hola@nio.cat</a> · <a href="/novetats">Novetats</a> · <a href="/privadesa">Privadesa</a></div>
|
|
144
|
+
<div class="page">© 2026 nio // un projecte de <a href="https://f.cat">f.cat</a> · <a href="mailto:hola@nio.cat">hola@nio.cat</a> · <a href="/qui-soc">Qui sóc</a> · <a href="/novetats">Novetats</a> · <a href="/privadesa">Privadesa</a></div>
|
|
145
145
|
</footer>
|
|
146
146
|
<script src="/bot.js" defer></script>
|
|
147
147
|
</body>
|
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-
|
|
2
|
+
const SHELL = 'nio-shell-v35';
|
|
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) => {
|