@xingjisoft/neutrino 0.3.0 → 0.4.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 (42) hide show
  1. package/README.md +312 -464
  2. package/assets/pro/backend_output_model.txt +5 -0
  3. package/assets/pro/claude-module-prompt.txt +65 -0
  4. package/assets/pro/common.txt +9 -0
  5. package/assets/pro/confirm_model.txt +5 -0
  6. package/assets/pro/front_output_model.txt +9 -0
  7. package/assets/pro/genimage_model.txt +1 -0
  8. package/assets/pro/main_model.txt +5 -0
  9. package/assets/pro/model-template.json +376 -0
  10. package/assets/pro/modeling_model.txt +5 -0
  11. package/assets/pro/plan_model.txt +8 -0
  12. package/assets/pro/thinking_model.txt +3 -0
  13. package/dist/app.js +43 -7
  14. package/dist/bin.js +5 -2
  15. package/dist/browser.js +57 -0
  16. package/dist/config.js +170 -26
  17. package/dist/index.js +1 -1
  18. package/dist/llm/approval.js +6 -2
  19. package/dist/llm/client.js +197 -28
  20. package/dist/llm/subagent.js +37 -1
  21. package/dist/pro/agents.js +104 -0
  22. package/dist/pro/config.js +88 -0
  23. package/dist/pro/preflight.js +172 -0
  24. package/dist/screens/chat.js +14 -8
  25. package/dist/screens/modelSelect.js +15 -4
  26. package/dist/screens/settings.js +9 -7
  27. package/dist/screens/start.js +48 -15
  28. package/dist/ui.js +58 -0
  29. package/dist/web/hub.js +22 -4
  30. package/dist/web/server.js +279 -66
  31. package/dist/web/session.js +258 -22
  32. package/dist/web/settings.js +91 -24
  33. package/package.json +37 -37
  34. package/web/README.md +87 -13
  35. package/web/app.js +304 -14
  36. package/web/i18n.js +382 -345
  37. package/web/index.html +90 -14
  38. package/web/neutrino-pro.gif +0 -0
  39. package/web/neutrino.png +0 -0
  40. package/web/pro.js +184 -0
  41. package/web/settings.js +322 -247
  42. package/web/style.css +297 -0
package/web/style.css CHANGED
@@ -137,6 +137,10 @@ svg {
137
137
  display: none !important;
138
138
  }
139
139
 
140
+ .pro-only { display: none; }
141
+ .pro-mode .pro-only { display: block; }
142
+ .pro-mode .standard-only { display: none !important; }
143
+
140
144
  #app {
141
145
  display: flex;
142
146
  width: 100%;
@@ -240,6 +244,120 @@ svg {
240
244
  flex: none;
241
245
  }
242
246
 
247
+ /* The orbit badge doubles as the switch to the Pro workspace. */
248
+ .brand-logo {
249
+ appearance: none;
250
+ padding: 0;
251
+ cursor: pointer;
252
+ transition: background var(--fast), border-color var(--fast), transform var(--fast);
253
+ }
254
+
255
+ .brand-logo:hover {
256
+ background: var(--surface-2);
257
+ border-color: var(--border-strong);
258
+ }
259
+
260
+ .brand-logo:active {
261
+ transform: scale(0.96);
262
+ }
263
+
264
+ .brand-logo:focus-visible {
265
+ outline: 2px solid var(--accent);
266
+ outline-offset: 2px;
267
+ }
268
+
269
+ /* Pro mode replaces the orbit with the static Neutrino wordmark and drops the
270
+ button cue. The PNG is transparent, so it sits directly on the sidebar. */
271
+ .brand-logo.pro-logo {
272
+ /* Narrower than the animated mark was: the brand name grows to "Neutrino
273
+ Pro" in this workspace, and the extra 30px keeps the name on one line at
274
+ the default sidebar width without touching the action buttons. */
275
+ width: 86px;
276
+ height: 25.8px;
277
+ cursor: default;
278
+ overflow: hidden;
279
+ background: transparent;
280
+ border-color: transparent;
281
+ border-radius: 0;
282
+ }
283
+
284
+ .brand-logo.pro-logo:hover {
285
+ background: transparent;
286
+ border-color: transparent;
287
+ }
288
+
289
+ .brand-logo.pro-logo:active {
290
+ transform: none;
291
+ }
292
+
293
+ .brand-logo.pro-logo img {
294
+ display: block;
295
+ width: 100%;
296
+ height: 100%;
297
+ object-fit: contain;
298
+ }
299
+
300
+ /* A dismissible hint under the brand; it never renders in Pro mode. It takes
301
+ its own row so it never covers the New conversation button, with the
302
+ triangle pointing back up at the badge. */
303
+ .pro-tip {
304
+ position: relative;
305
+ z-index: 20;
306
+ display: flex;
307
+ align-items: center;
308
+ gap: 6px;
309
+ flex: none;
310
+ margin: 10px 12px 0;
311
+ padding: 8px 8px 8px 12px;
312
+ background: #2563eb;
313
+ color: #ffffff;
314
+ border-radius: 10px;
315
+ box-shadow: 0 10px 26px rgba(0, 0, 0, 0.42);
316
+ font-size: 12px;
317
+ line-height: 1.4;
318
+ }
319
+
320
+ .pro-tip::before {
321
+ content: "";
322
+ position: absolute;
323
+ top: -6px;
324
+ left: 16px;
325
+ width: 0;
326
+ height: 0;
327
+ border-right: 6px solid transparent;
328
+ border-bottom: 6px solid #2563eb;
329
+ border-left: 6px solid transparent;
330
+ }
331
+
332
+ .pro-tip span {
333
+ flex: 1;
334
+ min-width: 0;
335
+ overflow-wrap: anywhere;
336
+ }
337
+
338
+ .pro-tip-close {
339
+ display: grid;
340
+ place-items: center;
341
+ flex: none;
342
+ width: 20px;
343
+ height: 20px;
344
+ padding: 0;
345
+ background: transparent;
346
+ border: 0;
347
+ border-radius: 6px;
348
+ color: rgba(255, 255, 255, 0.82);
349
+ }
350
+
351
+ .pro-tip-close:hover {
352
+ background: rgba(255, 255, 255, 0.16);
353
+ color: #ffffff;
354
+ }
355
+
356
+ .pro-tip-close svg {
357
+ width: 12px;
358
+ height: 12px;
359
+ }
360
+
243
361
  .icon-btn {
244
362
  display: grid;
245
363
  place-items: center;
@@ -516,6 +634,29 @@ svg {
516
634
  gap: 6px;
517
635
  }
518
636
 
637
+ /* Pro workspace only: a persistent way back to the regular console, pinned to
638
+ the far right of the main header. */
639
+ .pro-exit {
640
+ flex: none;
641
+ }
642
+
643
+ .pro-exit svg {
644
+ flex: none;
645
+ width: 14px;
646
+ height: 14px;
647
+ }
648
+
649
+ @media (max-width: 560px) {
650
+ .pro-exit {
651
+ width: 30px;
652
+ padding: 0;
653
+ }
654
+
655
+ .pro-exit span {
656
+ display: none;
657
+ }
658
+ }
659
+
519
660
  .badge {
520
661
  display: inline-flex;
521
662
  align-items: center;
@@ -1287,6 +1428,113 @@ svg {
1287
1428
  outline: none;
1288
1429
  }
1289
1430
 
1431
+ .pro-mode .composer-card { flex-wrap: wrap; }
1432
+ .pro-mode .composer textarea { flex: 1 0 100%; }
1433
+ .pro-preset-control { position: relative; margin-left: auto; align-self: center; }
1434
+ .pro-preset-control > summary {
1435
+ display: flex; align-items: center; gap: 10px; padding: 7px 10px;
1436
+ border-radius: var(--radius-sm); color: var(--text-secondary); cursor: pointer;
1437
+ font-size: 12px; list-style: none; user-select: none;
1438
+ }
1439
+ .pro-preset-control > summary::-webkit-details-marker { display: none; }
1440
+ .pro-preset-chevron { width: 14px; height: 14px; flex: none; transition: transform var(--fast); }
1441
+ .pro-preset-control[open] .pro-preset-chevron { transform: rotate(180deg); }
1442
+ .pro-preset-control > summary:hover, .pro-preset-control[open] > summary { background: var(--surface-3); }
1443
+ .pro-preset-card {
1444
+ position: absolute; z-index: 30; right: 0; bottom: calc(100% + 12px);
1445
+ width: min(390px, calc(100vw - 48px)); max-height: min(560px, 65dvh); overflow-y: auto;
1446
+ padding: 18px; border: 1px solid var(--border-strong); border-radius: var(--radius-lg);
1447
+ background: var(--surface-1); box-shadow: var(--shadow-strong);
1448
+ }
1449
+ .pro-preset-heading { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 18px; font-size: 13px; }
1450
+ .pro-preset-heading strong { color: var(--accent); font-size: 14px; font-weight: 500; }
1451
+ .pro-slider-label { display: block; color: var(--text-tertiary); font-size: 11px; margin-bottom: 8px; }
1452
+ .pro-preset-slider { position: relative; height: 40px; --pro-slider-position: 0; }
1453
+ .pro-preset-slider.is-disabled { opacity: .5; }
1454
+ .pro-slider-track {
1455
+ position: absolute; top: 8px; right: 2px; left: 2px; height: 24px;
1456
+ border-radius: 999px; background: #454547; overflow: hidden; pointer-events: none;
1457
+ }
1458
+ .pro-slider-fill {
1459
+ width: calc(12px + (100% - 24px) * var(--pro-slider-position)); height: 100%; background: #347ff6;
1460
+ }
1461
+ .pro-slider-stops {
1462
+ position: absolute; top: 18px; right: 14px; left: 14px; height: 4px; pointer-events: none;
1463
+ }
1464
+ .pro-slider-stop {
1465
+ position: absolute; width: 4px; height: 4px; transform: translateX(-50%);
1466
+ border-radius: 50%; background: rgba(255, 255, 255, .18);
1467
+ }
1468
+ .pro-slider-stop.is-filled { background: rgba(255, 255, 255, .35); }
1469
+ #pro-preset-range {
1470
+ position: relative; display: block; width: 100%; height: 40px; margin: 0; padding: 0;
1471
+ -webkit-appearance: none; appearance: none; border: 0; border-radius: 999px; background: transparent; cursor: pointer;
1472
+ }
1473
+ #pro-preset-range::-webkit-slider-runnable-track { height: 24px; border: 0; background: transparent; }
1474
+ #pro-preset-range::-webkit-slider-thumb {
1475
+ -webkit-appearance: none; appearance: none; width: 28px; height: 28px; margin-top: -2px;
1476
+ border: 0; border-radius: 50%; background: #fff;
1477
+ }
1478
+ #pro-preset-range::-moz-range-track { height: 24px; border: 0; background: transparent; }
1479
+ #pro-preset-range::-moz-range-progress { background: transparent; }
1480
+ #pro-preset-range::-moz-range-thumb { width: 28px; height: 28px; border: 0; border-radius: 50%; background: #fff; }
1481
+ #pro-preset-range:focus-visible { outline-offset: 2px; }
1482
+ #pro-preset-range:disabled { cursor: default; }
1483
+ .pro-preset-ticks { position: relative; height: 24px; margin: 6px 14px 16px; }
1484
+ .pro-preset-ticks button {
1485
+ position: absolute; transform: translateX(-50%); border: 0; background: transparent;
1486
+ color: var(--text-caption); font-size: 11px; line-height: 16px; padding: 4px; white-space: nowrap; cursor: pointer;
1487
+ }
1488
+ .pro-preset-ticks button[aria-pressed="true"] { color: var(--accent); }
1489
+ .pro-preset-ticks button:disabled { cursor: default; }
1490
+ .pro-slider-meteors { position: absolute; inset: 0; opacity: 0; pointer-events: none; }
1491
+ .pro-slider-meteor {
1492
+ position: absolute; top: 4px; left: -52px; width: 46px; height: 1.5px; opacity: 0;
1493
+ border-radius: 999px; background: linear-gradient(90deg, transparent, rgba(224, 222, 255, .7) 75%, #fff);
1494
+ animation: pro-slider-meteor 2.8s linear infinite; animation-play-state: paused;
1495
+ }
1496
+ .pro-slider-meteor::after {
1497
+ content: ""; position: absolute; top: 50%; right: -1px; width: 3px; height: 3px;
1498
+ transform: translateY(-50%); border-radius: 50%; background: #fff; box-shadow: 0 0 5px rgba(242, 232, 255, .8);
1499
+ }
1500
+ .pro-slider-meteor:nth-child(2) { top: 15px; width: 34px; animation-duration: 3.4s; animation-delay: -1.1s; }
1501
+ .pro-slider-meteor:nth-child(3) { top: 22px; width: 26px; animation-duration: 2.5s; animation-delay: -.45s; }
1502
+ .pro-slider-meteor:nth-child(4) { top: 8px; width: 20px; animation-duration: 4.2s; animation-delay: -2.3s; }
1503
+ .pro-preset-control.is-max .pro-slider-fill { background: linear-gradient(100deg, #347ff6 5%, #6075ff 50%, #a45af8); }
1504
+ .pro-preset-control.is-max .pro-slider-track { box-shadow: 0 0 14px rgba(132, 99, 255, .22); }
1505
+ .pro-preset-control.is-max .pro-slider-meteors { opacity: 1; }
1506
+ .pro-preset-control[open].is-max .pro-slider-meteor { animation-play-state: running; }
1507
+ .pro-preset-control.is-max .pro-preset-heading strong,
1508
+ .pro-preset-control.is-max .pro-preset-ticks button[aria-pressed="true"] { color: #bda8ff; }
1509
+ @keyframes pro-slider-meteor {
1510
+ 0% { transform: translate3d(0, 8px, 0) rotate(-14deg); opacity: 0; }
1511
+ 8% { opacity: .9; }
1512
+ 55% { opacity: .65; }
1513
+ 72%, 100% { transform: translate3d(450px, -8px, 0) rotate(-14deg); opacity: 0; }
1514
+ }
1515
+ .pro-model-details { border-top: 1px solid var(--border-soft); }
1516
+ .pro-model-details > summary {
1517
+ display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 10px 0;
1518
+ color: var(--text-tertiary); font-size: 11.5px; list-style: none; cursor: pointer; user-select: none;
1519
+ }
1520
+ .pro-model-details > summary::-webkit-details-marker { display: none; }
1521
+ .pro-model-details > summary:hover { color: var(--text); }
1522
+ .pro-model-details-chevron { width: 14px; height: 14px; flex: none; color: var(--text-caption); transition: transform var(--fast); }
1523
+ .pro-model-details[open] .pro-model-details-chevron { transform: rotate(180deg); }
1524
+ .pro-preset-models { display: grid; gap: 9px; padding: 2px 0 12px; }
1525
+ .pro-model-row { display: grid; grid-template-columns: minmax(90px, .8fr) minmax(0, 1.6fr); gap: 12px; font-size: 11.5px; line-height: 1.5; }
1526
+ .pro-model-row > span:first-child { color: var(--text-tertiary); }
1527
+ .pro-model-row > span:last-child { overflow-wrap: anywhere; text-align: right; }
1528
+ .pro-model-row .pro-model-fallback { color: var(--amber); }
1529
+ .pro-preset-card .settings-note { margin-top: 15px; font-size: 11px; }
1530
+ .pro-preset-card .form-feedback:empty { display: none; }
1531
+ .pro-fallback-rows { display: grid; gap: 14px; margin-top: 18px; }
1532
+ .pro-fallback-row { display: grid; gap: 5px; padding-bottom: 14px; border-bottom: 1px solid var(--border-soft); overflow-wrap: anywhere; }
1533
+ .pro-fallback-row strong { font-size: 13px; font-weight: 500; }
1534
+ .pro-fallback-row > span { color: var(--amber); font-size: 12px; }
1535
+ .pro-fallback-row small { color: var(--text-tertiary); font-size: 11px; }
1536
+ .pro-fallback-dialog { max-width: 540px; }
1537
+
1290
1538
  #send {
1291
1539
  display: grid;
1292
1540
  place-items: center;
@@ -2743,6 +2991,45 @@ select option {
2743
2991
  background: transparent;
2744
2992
  }
2745
2993
 
2994
+ /* --------------------------------------------------------------- pro curtain */
2995
+
2996
+ /* Set by the inline head bootstrap when a just-started switch is recorded. It
2997
+ covers /pro before the module runs, so there is no flash of the new page
2998
+ while the curtain is being rebuilt. The module removes it immediately. */
2999
+ html.pro-transitioning body::before {
3000
+ content: "";
3001
+ position: fixed;
3002
+ inset: 0;
3003
+ z-index: 299;
3004
+ background: #151515;
3005
+ }
3006
+
3007
+ /* Full-screen cover used while switching to /pro. #151515 matches the GIF's
3008
+ own background, so the wordmark blends into the cover on first paint. */
3009
+ .pro-curtain {
3010
+ position: fixed;
3011
+ inset: 0;
3012
+ z-index: 300;
3013
+ display: flex;
3014
+ align-items: center;
3015
+ justify-content: center;
3016
+ background: #151515;
3017
+ opacity: 0;
3018
+ transition: opacity 0.35s ease;
3019
+ }
3020
+
3021
+ .pro-curtain.pro-curtain-visible {
3022
+ opacity: 1;
3023
+ }
3024
+
3025
+ .pro-curtain img {
3026
+ display: block;
3027
+ width: min(82vw, 900px);
3028
+ max-height: 60vh;
3029
+ height: auto;
3030
+ object-fit: contain;
3031
+ }
3032
+
2746
3033
  /* -------------------------------------------------------------- animations */
2747
3034
 
2748
3035
  @keyframes blink {
@@ -2781,6 +3068,7 @@ select option {
2781
3068
  }
2782
3069
 
2783
3070
  @media (prefers-reduced-motion: reduce) {
3071
+ .pro-slider-meteors { display: none; }
2784
3072
  *,
2785
3073
  *::before,
2786
3074
  *::after {
@@ -2804,6 +3092,15 @@ select option {
2804
3092
  }
2805
3093
  }
2806
3094
 
3095
+ /* Pro's brand name is longer ("Neutrino Pro"). Keep the regular 288px sidebar
3096
+ down to phone width so the name stays on one line next to the narrower logo;
3097
+ the regular workspace keeps its more compact 264px pane. */
3098
+ @media (min-width: 761px) and (max-width: 1080px) {
3099
+ body.pro-mode #sidebar {
3100
+ width: 288px;
3101
+ }
3102
+ }
3103
+
2807
3104
  /* Phones use one full-width pane: the conversation list opens first. */
2808
3105
  @media (max-width: 760px) {
2809
3106
  #sidebar {