beatrina 0.9.43 → 1.0.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 (44) hide show
  1. package/app/Beatrina.app/Contents/Info.plist +86 -0
  2. package/app/Beatrina.app/Contents/MacOS/beatrina +0 -0
  3. package/app/Beatrina.app/Contents/Resources/icon.icns +0 -0
  4. package/app/beatrina.png +0 -0
  5. package/app/linux-arm64/beatrina +0 -0
  6. package/app/win-x64/Beatrina-setup.exe +0 -0
  7. package/beatrina_V1.0.0.html +1906 -0
  8. package/beatrina_V1.0.0.html.inputs.json +1 -0
  9. package/bin/beatrina.mjs +200 -12
  10. package/bin/cli.mjs +8 -3
  11. package/bin/finder.mjs +1 -1
  12. package/bin/shortcut.mjs +15 -15
  13. package/build-info.json +1 -1
  14. package/host/engine-node-job.mjs +35 -0
  15. package/host/engine-pool.mjs +153 -26
  16. package/host/engine-r.mjs +8 -1
  17. package/host/jobs/document-child.mjs +271 -0
  18. package/host/journal-store.mjs +19 -3
  19. package/host/latex-compile.mjs +673 -0
  20. package/host/main.mjs +58 -18
  21. package/host/planes/analyze.mjs +18 -2
  22. package/host/planes/choose.mjs +13 -2
  23. package/host/planes/expect-term.mjs +102 -0
  24. package/host/planes/jobs.mjs +38 -5
  25. package/host/planes/sessions.mjs +5 -1
  26. package/host/planes/terminal.mjs +23 -9
  27. package/host/pwa/icon-192.png +0 -0
  28. package/host/pwa/icon-512.png +0 -0
  29. package/host/quarto.mjs +118 -0
  30. package/host/server.mjs +83 -2
  31. package/host/worker-plane.mjs +7 -1
  32. package/kernel/check/acceptance.mjs +43 -12
  33. package/kernel/check/knit-file.mjs +300 -52
  34. package/kernel/check/session.mjs +19 -4
  35. package/kernel/kernel-version +1 -1
  36. package/kernel/knitr-run.R +8 -1
  37. package/kernel/mcp/beatrina-mcp.mjs +6 -5
  38. package/kernel/worker.R +21 -1
  39. package/lib/engine-labels.js +7 -1
  40. package/menu/Beatrina Menu.app/Contents/Info.plist +2 -2
  41. package/menu/Beatrina Menu.app/Contents/MacOS/Beatrina Menu +0 -0
  42. package/package.json +4 -3
  43. package/beatrina_V0.9.43.html +0 -1885
  44. package/beatrina_V0.9.43.html.inputs.json +0 -1
@@ -1,3 +1,29 @@
1
+ var __getOwnPropNames = Object.getOwnPropertyNames;
2
+ var __esm = (fn, res, err) => function __init() {
3
+ if (err) throw err[0];
4
+ try {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ } catch (e) {
7
+ throw err = [e], e;
8
+ }
9
+ };
10
+
11
+ // lib/engine-labels.js
12
+ var KNOWN_ENGINES, BY_KEY, AUTHORING_ENGINES;
13
+ var init_engine_labels = __esm({
14
+ "lib/engine-labels.js"() {
15
+ KNOWN_ENGINES = Object.freeze([
16
+ Object.freeze({ key: "r", label: "R" }),
17
+ Object.freeze({ key: "python", label: "Python" }),
18
+ Object.freeze({ key: "js", label: "JavaScript" }),
19
+ Object.freeze({ key: "bash", label: "Bash" }),
20
+ Object.freeze({ key: "sql", label: "SQL" })
21
+ ]);
22
+ BY_KEY = new Map(KNOWN_ENGINES.map((e) => [e.key, e.label]));
23
+ AUTHORING_ENGINES = Object.freeze(KNOWN_ENGINES.map((e) => e.key));
24
+ }
25
+ });
26
+
1
27
  // tools/beatrina/knit-file.mjs
2
28
  import fs from "node:fs";
3
29
  import path from "node:path";
@@ -1629,10 +1655,18 @@ var BEATRINA_CSS = `
1629
1655
  .cn-root .beatrina-ai-capability{
1630
1656
  border:1px solid transparent;padding:3px 9px;
1631
1657
  }
1632
- /* Sheet-layout-only controls (lib/ai-code-button.js builds them for every
1633
- chunk; the sheet block near the app themes shows them). */
1634
- .cn-root .beatrina-ai-capability-one,.cn-root .beatrina-ai-destination-toggle{display:none;}
1635
- .cn-root .beatrina-ai-capability-one .beatrina-ai-quickwrite-caret{font-size:10px;opacity:.7;}
1658
+ /* ONE entrance in every layout (2026-09-22): "Code \u25BE" \u2014 the word acts, the
1659
+ arrow holds Chat and Plan. The three capability buttons stay in the DOM,
1660
+ hidden; the destination toggle is still the sheet layout's alone. */
1661
+ .cn-root .beatrina-ai-capability{display:none;}
1662
+ .cn-root .beatrina-ai-destination-toggle{display:none;}
1663
+ .cn-root .beatrina-ai-capability-one,.cn-root .beatrina-ai-capability-more{
1664
+ display:inline-flex;align-items:center;color:#fff;background:var(--cn-accent,#4e79a7);border:1px solid transparent;
1665
+ }
1666
+ .cn-root .beatrina-ai-capability-one{font-weight:750;padding:3px 8px 3px 9px;border-radius:6px 0 0 6px;margin-right:0;}
1667
+ .cn-root .beatrina-ai-capability-more{padding:3px 6px;border-radius:0 6px 6px 0;margin-left:-1px;border-left:1px solid rgba(255,255,255,.35);}
1668
+ .cn-root .beatrina-ai-capability-one:hover:not(:disabled),.cn-root .beatrina-ai-capability-more:hover:not(:disabled){color:#fff;background:var(--cn-accent-deep,#3a6a9f);}
1669
+ .cn-root .beatrina-ai-capability-more .beatrina-ai-quickwrite-caret{font-size:10px;opacity:.9;pointer-events:none;}
1636
1670
  .cn-root .beatrina-ai-capability[data-intent="ask"]{
1637
1671
  color:var(--cn-accent-deep,#3a6a9f);background:transparent;
1638
1672
  }
@@ -2327,6 +2361,12 @@ var BEATRINA_CSS = `
2327
2361
  background:var(--cn-control-bg);color:var(--cn-text);
2328
2362
  }
2329
2363
  .cn-root .beatrina-prompt-input:focus{outline:none;border-color:var(--cn-accent);box-shadow:var(--cn-focus-ring);}
2364
+ /* The multi-line form (Writing mode's Edit source): Markdown, so the mono
2365
+ face and the size a source is read at; resizable downward only. */
2366
+ .cn-root textarea.beatrina-prompt-input{
2367
+ font-family:var(--cn-mono);font-size:var(--cn-fs);line-height:1.5;
2368
+ min-width:min(560px, 80vw);resize:vertical;
2369
+ }
2330
2370
 
2331
2371
  /* \u2500\u2500 the prompt editor \u2014 the words, and the right to change them \u2500\u2500 */
2332
2372
  .cn-root .beatrina-modal-panel.is-prompts{width:min(860px,95vw);}
@@ -3370,6 +3410,11 @@ var BEATRINA_CSS = `
3370
3410
  color:rgba(255,255,255,.72);font:12px var(--cn-font);pointer-events:none;
3371
3411
  animation:beatrina-focus-in .26s ease;
3372
3412
  }
3413
+ /* A Text cell on its own: a LIGHT, quiet page around the prose (owner,
3414
+ 2026-09-22: "it is all dark"). The code chunk keeps the dark haze. */
3415
+ .cn-root.beatrina-focus-prose .beatrina-focus-haze{background:color-mix(in srgb, var(--cn-bg,#fff) 94%, transparent);}
3416
+ .cn-root.beatrina-focus-prose .beatrina-focus-note{color:var(--cn-text-dim,#666);}
3417
+ .cn-root.beatrina-focus-prose .cell.beatrina-focus-cell{box-shadow:0 1px 0 var(--cn-border,#e3e7ef),0 12px 40px rgba(0,0,0,.08);}
3373
3418
  /* Narrower than the notebook, and a WORKING surface rather than a card.
3374
3419
  660px with the chunk's own height left a three-line chunk as a small box
3375
3420
  adrift in a large dark screen (owner, 2026-09-11: "This is so small"). The
@@ -3721,6 +3766,65 @@ var BEATRINA_CSS = `
3721
3766
  }
3722
3767
  .cn-root .beatrina-source-view .beatrina-highlight .mdh-yaml{color:var(--cn-text);}
3723
3768
 
3769
+ /* \u2500\u2500 Source mode reads like a README on GitHub (owner, 2026-09-22: "the
3770
+ source mode is ugly and horrible, even reading a readme on github is
3771
+ better") \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
3772
+ Everything below is DECORATION of the same bytes: no glyph changes and no
3773
+ line moves, so the transparent textarea stays exactly over its colour.
3774
+
3775
+ A reading width. At Full page width a prose line ran 1,400 px; a README is
3776
+ read at about a thousand. Centred in whatever the page gives it. */
3777
+ .cn-root .beatrina-source-view{max-width:min(100%, 1080px);margin:0 auto;}
3778
+ /* Prose always wraps here, whatever the code-chunk wrap setting says
3779
+ (lib/source-view.js passes softWrap: "always" to the surface, which makes
3780
+ the gutter follow the wrapped rows). */
3781
+ .cn-root .beatrina-source-view textarea.beatrina-code,
3782
+ .cn-root .beatrina-source-view .beatrina-highlight{white-space:pre-wrap;overflow-wrap:break-word;}
3783
+ .cn-root .beatrina-source-view textarea.beatrina-code{overflow-x:hidden;}
3784
+ .cn-root .beatrina-source-view .beatrina-highlight > span{display:block;}
3785
+ .cn-root .beatrina-source-view .beatrina-code-wrap::after,
3786
+ .cn-root .beatrina-source-view .beatrina-code-wrap::before{content:none;}
3787
+ /* Code blocks and the front matter on a band (lib/qmd-language.js names
3788
+ each line's region). Widened by a negative margin and an equal padding, so
3789
+ the band reaches past the text while the text itself stays exactly where
3790
+ the textarea puts it and wraps at the same width. */
3791
+ .cn-root .beatrina-source-view{
3792
+ --src-code-bg:color-mix(in srgb, var(--cn-text) 5%, var(--ed-bg, var(--cn-surface)));
3793
+ --src-yaml-bg:color-mix(in srgb, var(--cn-text) 3%, var(--ed-bg, var(--cn-surface)));
3794
+ }
3795
+ .cn-root .beatrina-source-view .beatrina-highlight > span:is(.qmd-open, .qmd-code, .qmd-close),
3796
+ .cn-root .beatrina-source-view .beatrina-highlight > span.qmd-yaml{
3797
+ margin:0 -10px;padding:0 10px;background:var(--src-code-bg);
3798
+ }
3799
+ .cn-root .beatrina-source-view .beatrina-highlight > span.qmd-yaml{background:var(--src-yaml-bg);}
3800
+ /* A line is 13 \xD7 1.62 = 21.06 px tall, so neighbouring bands meet on a
3801
+ fraction of a pixel and the page shows through as a hairline seam. Each
3802
+ band line casts its own colour one pixel down to close it (not the last). */
3803
+ .cn-root .beatrina-source-view .beatrina-highlight > span:is(.qmd-open, .qmd-code){box-shadow:0 1px 0 var(--src-code-bg);}
3804
+ .cn-root .beatrina-source-view .beatrina-highlight > span.qmd-yaml:has(+ span.qmd-yaml){box-shadow:0 1px 0 var(--src-yaml-bg);}
3805
+ .cn-root .beatrina-source-view .beatrina-highlight > span.qmd-open{border-radius:6px 6px 0 0;}
3806
+ .cn-root .beatrina-source-view .beatrina-highlight > span.qmd-close{border-radius:0 0 6px 6px;}
3807
+ .cn-root .beatrina-source-view .beatrina-highlight > span.qmd-open + span.qmd-close{border-radius:6px;}
3808
+ /* Headings in the editor theme's function colour, so they lead in every
3809
+ theme \u2014 in Plain the accent is graphite and a heading read as bold text. */
3810
+ .cn-root .beatrina-source-view .beatrina-highlight .mdh-h{color:var(--rtok-fn, var(--cn-accent-deep));}
3811
+ /* Copy all above the text; Copy beside each block's opening fence
3812
+ (lib/source-copy.js). Quiet until pointed at; a done copy says so in words
3813
+ and in the Okabe-Ito green \u2014 never by colour alone. */
3814
+ .cn-root .beatrina-src-actions{display:flex;justify-content:flex-end;margin:0 0 6px;}
3815
+ .cn-root .beatrina-src-copies{position:absolute;inset:0;pointer-events:none;z-index:3;}
3816
+ .cn-root .beatrina-src-copy{
3817
+ display:inline-flex;align-items:center;gap:5px;height:22px;padding:0 8px;
3818
+ font:500 var(--cn-fs-xs,11px)/1 var(--cn-font);color:var(--cn-text-muted);
3819
+ background:var(--cn-surface);border:1px solid var(--cn-border);border-radius:6px;cursor:pointer;
3820
+ }
3821
+ .cn-root .beatrina-src-copies .beatrina-src-copy{position:absolute;right:4px;margin-top:-1px;pointer-events:auto;opacity:.62;}
3822
+ .cn-root .beatrina-src-copy svg{width:13px;height:13px;flex:none;}
3823
+ .cn-root .beatrina-src-copy:hover,.cn-root .beatrina-src-copy:focus-visible{opacity:1;color:var(--cn-accent-deep);border-color:var(--cn-control-border);}
3824
+ .cn-root .beatrina-src-copy:focus-visible{outline:2px solid var(--cn-accent);outline-offset:1px;}
3825
+ .cn-root .beatrina-src-copy.is-done{opacity:1;color:#009E73;border-color:#009E73;}
3826
+ @media print{.cn-root .beatrina-src-actions,.cn-root .beatrina-src-copies{display:none!important;}}
3827
+
3724
3828
  /* \u2500\u2500 result bar: fold / clear \u2500\u2500 */
3725
3829
  .cn-root .beatrina-output-bar{
3726
3830
  display:flex;align-items:center;gap:5px;margin:3px 0 0;
@@ -4389,7 +4493,10 @@ var BEATRINA_CSS = `
4389
4493
  .cn-root .beatrina-md-tool:focus-visible{outline:0;box-shadow:var(--cn-focus-ring);}
4390
4494
  .cn-root .beatrina-md-tool.is-cite{color:var(--cn-accent);}
4391
4495
  .cn-root .beatrina-md-tool-label{font:600 var(--cn-fs-sm) var(--cn-font);}
4392
- .cn-root .beatrina-md-bar-sp{flex:0 0 4px;}
4496
+ /* width as well as flex-basis: WebKit leaves a basis-only item out of the bar's max-content
4497
+ width, so the bar came out 4 px short at every fold level and folded everything behind \u22EE
4498
+ (Beatrina's window, 2026-09-23). */
4499
+ .cn-root .beatrina-md-bar-sp{flex:0 0 4px;width:4px;}
4393
4500
  .cn-root .beatrina-md-count{
4394
4501
  font-size:var(--cn-fs-sm);opacity:.55;font-variant-numeric:tabular-nums;margin-right:2px;
4395
4502
  }
@@ -4471,10 +4578,10 @@ var BEATRINA_CSS = `
4471
4578
  /* The row's fixed groups leave the bar roughly (viewport \u2212 1000) px. It
4472
4579
  gives up its least essential pixels first, and every group it drops is
4473
4580
  still on the \u22EE shelf, labelled, with its key \u2014 a narrow window costs a
4474
- click, never a command. Order of surrender: the Cite word, the count,
4475
- breathing room, the insert group, the blocks group. */
4581
+ click, never a command. Order of surrender: the count, breathing room,
4582
+ the insert group, the blocks group. (Cite is a word with no glyph now, so
4583
+ it is never shrunk to one.) */
4476
4584
  @media (max-width:1640px){
4477
- .cn-root header .beatrina-md-bar .is-cite .beatrina-md-tool-label{display:none;}
4478
4585
  .cn-root header .beatrina-md-bar .beatrina-md-count{display:none;}
4479
4586
  }
4480
4587
  @media (max-width:1560px){
@@ -4493,7 +4600,7 @@ var BEATRINA_CSS = `
4493
4600
  .cn-root header .beatrina-md-bar[data-fold="3"] .beatrina-md-group.is-insert,
4494
4601
  .cn-root header .beatrina-md-bar[data-fold="3"] .beatrina-md-group.is-blocks,
4495
4602
  .cn-root header .beatrina-md-bar[data-fold="3"] .beatrina-md-group.is-history{display:none;}
4496
- .cn-root header .beatrina-md-bar[data-fold="4"] .beatrina-md-group:not(.is-text),
4603
+ .cn-root header .beatrina-md-bar[data-fold="4"] .beatrina-md-group:not(.is-text):not(.is-cite-lead),
4497
4604
  .cn-root header .beatrina-md-bar[data-fold="4"] .is-text .beatrina-md-tool:not([data-command="prose.bold"]):not([data-command="prose.italic"]),
4498
4605
  .cn-root header .beatrina-md-bar[data-fold="5"] .beatrina-md-group{display:none;}
4499
4606
  /* At the last fold what is left (the \u22EE shelf and Done) is the bar's minimum:
@@ -4545,7 +4652,7 @@ var BEATRINA_CSS = `
4545
4652
  .cn-root .beatrina-md-morebar{
4546
4653
  display:block; /* .beatrina-pop is a column flexbox, and a flexbox has no CSS columns */
4547
4654
  columns:var(--beatrina-menu-cols,2);column-gap:14px;column-rule:1px solid var(--cn-border-soft);
4548
- width:min(calc(var(--beatrina-menu-cols,2) * 226px + 30px), calc(100vw - 24px));
4655
+ width:min(calc(var(--beatrina-menu-cols,2) * var(--beatrina-menu-colw) + 30px), calc(100vw - 24px));
4549
4656
  max-height:calc(100vh - 100px);overflow-y:auto;padding:6px 8px 2px;
4550
4657
  }
4551
4658
  .cn-root .beatrina-md-pop-group{break-inside:avoid;-webkit-column-break-inside:avoid;margin:0 0 8px;}
@@ -4851,18 +4958,22 @@ var BEATRINA_CSS = `
4851
4958
  .cn-root .cell.beatrina-md-cell:focus-within .beatrina-cell-rail{
4852
4959
  pointer-events:auto;
4853
4960
  }
4961
+ /* A bit larger (owner, 2026-09-22): 24px buttons, 15px glyphs \u2014 the most the 32px gutter band holds. */
4854
4962
  .cn-root .beatrina-cell-rail-btn{
4855
4963
  position:relative;
4856
4964
  display:flex;align-items:center;justify-content:center;
4857
- width:21px;height:21px;padding:0;border:0;border-radius:6px;
4965
+ width:24px;height:24px;padding:0;border:0;border-radius:7px;
4858
4966
  background:transparent;cursor:pointer;
4859
- color:color-mix(in srgb, var(--cn-text) 48%, transparent);
4967
+ color:color-mix(in srgb, var(--cn-text) 55%, transparent);
4860
4968
  transition:color var(--cn-dur-1),background var(--cn-dur-1);
4861
4969
  }
4862
4970
  .cn-root .beatrina-cell-rail-btn svg{
4863
- width:14px;height:14px;
4971
+ width:15px;height:15px;
4864
4972
  transition:transform var(--cn-dur-1);
4865
4973
  }
4974
+ /* The lock, once on, stays visible: a locked cell says so without a hover. */
4975
+ .cn-root .cell.locked .beatrina-cell-rail{pointer-events:auto;}
4976
+ .cn-root .cell.locked .beatrina-cell-rail-btn[data-command="text.lock"]{color:var(--cn-accent-deep);opacity:1;}
4866
4977
  .cn-root .beatrina-cell-rail-btn:hover{
4867
4978
  background:color-mix(in srgb, var(--cn-accent) 13%, transparent);
4868
4979
  color:var(--cn-accent-deep);
@@ -5117,6 +5228,8 @@ var BEATRINA_CSS = `
5117
5228
  font:700 var(--cn-fs-sm,12.5px) var(--cn-font);border:1px solid #E69F00;background:#E69F00;color:#1a1300;}
5118
5229
  .cn-root .beatrina-tolatex:hover{filter:brightness(1.06);}
5119
5230
  .cn-root .beatrina-tolatex:focus-visible{outline:2px solid var(--beatrina-hdr-focus,#fff);outline-offset:2px;}
5231
+ /* "Open <stem>.tex": a file name can be long; the button keeps its row. */
5232
+ .cn-root .beatrina-tolatex.is-open{max-width:220px;overflow:hidden;text-overflow:ellipsis;}
5120
5233
  /* In LaTeX the same place SAYS where you are \u2014 green, a dot as well as the
5121
5234
  colour, and not a button any more. */
5122
5235
  .cn-root .beatrina-tolatex.is-latex{background:#009E73;border-color:#009E73;color:#fff;cursor:default;}
@@ -5168,10 +5281,13 @@ var BEATRINA_CSS = `
5168
5281
  Import Data \xB7 Save drop their words) makes the room \u2014 the document's
5169
5282
  name outranks three verbs that keep their icons. */
5170
5283
  .cn-root .hdr-row-id .beatrina-docsw{order:7;flex:0 1 auto;min-width:120px;position:relative;}
5284
+ /* A BUTTON you can see (owner, 2026-09-22: "it should be open documents and
5285
+ should be clearer"): the header's own button face, the words on it, not a
5286
+ faint pill with a glyph. */
5171
5287
  .cn-root .hdr-row-id .beatrina-docsw-btn{
5172
- display:inline-flex;align-items:center;gap:7px;height:26px;max-width:min(360px,100%);min-width:0;padding:0 9px 0 7px;
5173
- border-radius:999px;border:1px solid var(--beatrina-hdr-line);background:transparent;box-shadow:none;
5174
- color:var(--beatrina-hdr-fg);font:500 var(--cn-fs-xs) var(--cn-font);cursor:pointer;
5288
+ display:inline-flex;align-items:center;gap:8px;height:30px;max-width:min(360px,100%);min-width:0;padding:0 12px 0 10px;
5289
+ border-radius:8px;border:1px solid var(--beatrina-hdr-line);background:var(--beatrina-hdr-wash);box-shadow:none;
5290
+ color:var(--beatrina-hdr-fg);font:600 var(--cn-fs-sm,12.5px) var(--cn-font);cursor:pointer;
5175
5291
  }
5176
5292
  .cn-root .hdr-row-id .beatrina-docsw-btn:hover{background:var(--beatrina-hdr-wash);}
5177
5293
  .cn-root .hdr-row-id .beatrina-docsw-btn[aria-expanded="true"]{background:var(--beatrina-hdr-wash-2);}
@@ -5181,38 +5297,102 @@ var BEATRINA_CSS = `
5181
5297
  }
5182
5298
  .cn-root .beatrina-docsw-kind.is-notebook{border-color:transparent;background:var(--beatrina-hdr-wash-2);color:var(--beatrina-hdr-fg);}
5183
5299
  .cn-root .beatrina-docsw-name{font:600 11px var(--cn-mono);color:var(--beatrina-hdr-fg);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
5300
+ .cn-root .beatrina-docsw-word{font:600 var(--cn-fs-sm,12.5px) var(--cn-font);color:var(--beatrina-hdr-fg);white-space:nowrap;}
5184
5301
  .cn-root .beatrina-docsw-count{font:500 var(--cn-fs-xs) var(--cn-font);color:var(--beatrina-hdr-fg-faint);white-space:nowrap;}
5185
5302
  .cn-root .beatrina-docsw-count:empty{display:none;}
5186
5303
  .cn-root .beatrina-docsw-caret{font-size:9px;color:var(--beatrina-hdr-fg-faint);}
5187
- .cn-root .beatrina-docsw .hdr-export-drop{left:auto;right:0;width:660px;max-width:calc(100vw - 24px);}
5188
- .cn-root .beatrina-docsw-drop .beatrina-docsw-badge{border-color:var(--cn-border);color:var(--cn-text-dim);background:var(--cn-surface);}
5304
+ .cn-root .beatrina-docsw .hdr-export-drop{left:auto;right:0;width:540px;max-width:calc(100vw - 24px);}
5305
+ .cn-root .beatrina-docsw-drop .beatrina-docsw-badge{border-color:var(--cn-border);color:var(--cn-text-dim);background:var(--cn-surface);min-width:26px;height:20px;font-size:10px;}
5306
+ .cn-root .beatrina-docsw-drop .beatrina-docsw-head{display:flex;align-items:center;justify-content:space-between;gap:8px;white-space:nowrap;}
5307
+ .cn-root .beatrina-docsw-head-note{font:500 10.5px var(--cn-font);color:var(--cn-text-dim);text-transform:none;letter-spacing:0;}
5308
+ .cn-root .beatrina-docsw-list{display:flex;flex-direction:column;gap:0;padding:2px 0;}
5309
+ .cn-root .beatrina-docsw-list > * + *{border-top:1px solid var(--cn-border-soft,#eef1f5);}
5310
+ /* A drawn icon per kind, the kind's badge under it. */
5311
+ .cn-root .beatrina-docsw-icon{display:inline-flex;flex-direction:column;align-items:center;gap:2px;width:36px;flex:0 0 36px;color:var(--cn-text-dim);}
5312
+ .cn-root .beatrina-docsw-icon svg{width:18px;height:18px;}
5313
+ .cn-root .beatrina-docsw-icon .beatrina-docsw-badge{min-width:0;height:14px;padding:0 4px;font-size:8.5px;}
5189
5314
  .cn-root .beatrina-docsw-drop .beatrina-docsw-row{
5190
- display:grid;grid-template-columns:24px 8px minmax(0,1fr) auto 18px;grid-template-rows:auto auto;
5191
- align-items:center;column-gap:8px;row-gap:1px;text-align:left;width:100%;padding:6px 8px;margin:0;
5192
- border:0;border-radius:6px;background:transparent;color:var(--cn-text);cursor:pointer;font:var(--cn-fs-sm) var(--cn-font);
5315
+ display:grid;grid-template-columns:36px minmax(0,1fr) auto 30px;grid-template-rows:auto auto;
5316
+ align-items:center;column-gap:10px;row-gap:2px;text-align:left;width:100%;padding:8px 10px;margin:0;
5317
+ border:0;border-radius:8px;background:transparent;color:var(--cn-text);cursor:pointer;font:var(--cn-fs-sm) var(--cn-font);
5193
5318
  }
5194
5319
  .cn-root .beatrina-docsw-drop .beatrina-docsw-row:hover{background:var(--cn-control-bg);}
5195
5320
  .cn-root .beatrina-docsw-drop .beatrina-docsw-row:focus-visible{outline:2px solid var(--cn-accent);outline-offset:-2px;}
5196
- .cn-root .beatrina-docsw-drop .beatrina-docsw-row[aria-current="true"]{background:var(--cn-accent-soft,#eef3f8);}
5321
+ /* The one on screen: a wash, a bar at its edge, and the words "on screen" \u2014 never colour alone. */
5322
+ .cn-root .beatrina-docsw-drop .beatrina-docsw-row[aria-current="true"]{background:var(--cn-accent-soft,#eef3f8);box-shadow:inset 3px 0 0 var(--cn-accent);}
5323
+ /* Every cell PLACED, not auto-flowed: the path in row 2 of column 2 sat under
5324
+ the chip's would-be column, so auto-placement pushed the chip one column
5325
+ right and the \xD7 into an implicit fifth \u2014 measured 36px 374px 0 30px 28px. */
5326
+ .cn-root .beatrina-docsw-drop .beatrina-docsw-row > .beatrina-docsw-icon{grid-column:1;grid-row:1 / span 2;}
5327
+ .cn-root .beatrina-docsw-drop .beatrina-docsw-row > .beatrina-docsw-mode{grid-column:3;grid-row:1 / span 2;}
5328
+ .cn-root .beatrina-docsw-drop .beatrina-docsw-row > .beatrina-docsw-x{grid-column:4;grid-row:1 / span 2;}
5329
+ .cn-root .beatrina-docsw-main{grid-column:2;grid-row:1;display:inline-flex;align-items:center;gap:7px;min-width:0;}
5330
+ .cn-root .beatrina-docsw-here{font:500 10.5px var(--cn-font);color:var(--cn-accent);white-space:nowrap;}
5197
5331
  .cn-root .beatrina-docsw-dot{width:7px;height:7px;border-radius:50%;border:1px solid var(--cn-border);background:transparent;}
5198
5332
  .cn-root .beatrina-docsw-dot.is-dirty{background:#E69F00;border-color:#E69F00;}
5199
5333
  .cn-root .beatrina-docsw-dot.is-running{background:#56B4E9;border-color:#56B4E9;}
5200
- .cn-root .beatrina-docsw-drop .beatrina-docsw-label{font:600 11.5px var(--cn-mono);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
5334
+ .cn-root .beatrina-docsw-drop .beatrina-docsw-label{font:600 12.5px var(--cn-font);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
5201
5335
  .cn-root .beatrina-docsw-mode{
5202
5336
  font:600 9.5px var(--cn-font);letter-spacing:.06em;text-transform:uppercase;color:var(--cn-text-dim);
5203
5337
  border:1px solid var(--cn-border);border-radius:4px;padding:1px 4px;line-height:1.2;white-space:nowrap;
5204
5338
  }
5205
5339
  .cn-root .beatrina-docsw-mode:empty{visibility:hidden;}
5206
5340
  .cn-root .beatrina-docsw-mode.is-writing{color:#8e5b00;border-color:#e6c47a;}
5207
- .cn-root .beatrina-docsw-x{color:var(--cn-text-dim);font-size:14px;line-height:1;padding:0 3px;border-radius:3px;text-align:center;}
5208
- .cn-root .beatrina-docsw-x:hover{color:#c5221f;background:#fce8e6;}
5209
- .cn-root .beatrina-docsw-path{grid-column:3 / span 3;grid-row:2;font:10.5px var(--cn-font);color:var(--cn-text-dim);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
5341
+ .cn-root .beatrina-docsw-mode.is-on{color:var(--cn-accent);border-color:var(--cn-accent);}
5342
+ /* A Close you can see: a bordered square on every row. */
5343
+ .cn-root .beatrina-docsw-x{
5344
+ width:28px;height:28px;display:inline-flex;align-items:center;justify-content:center;
5345
+ border:1px solid var(--cn-border);border-radius:7px;background:var(--cn-surface);
5346
+ color:var(--cn-text-dim);font-size:16px;line-height:1;text-align:center;
5347
+ }
5348
+ .cn-root .beatrina-docsw-x:hover{color:#c5221f;border-color:#c5221f;background:#fce8e6;}
5349
+ .cn-root .beatrina-docsw-x.is-off,.cn-root .beatrina-docsw-x.is-off:hover{opacity:.35;cursor:default;color:var(--cn-text-dim);border-color:var(--cn-border);background:var(--cn-surface);}
5350
+ .cn-root .beatrina-docsw-sub{grid-column:2;grid-row:2;display:flex;align-items:baseline;gap:5px;min-width:0;}
5351
+ .cn-root .beatrina-docsw-path{flex:0 1 auto;min-width:0;font:10.5px var(--cn-font);color:var(--cn-text-dim);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
5352
+ /* The footer: ONE row \u2014 New \u25BE \xB7 Open\u2026 on the left, Close apart on the right; an icon and a word each. */
5353
+ .cn-root .beatrina-docsw-foot{display:flex;flex-wrap:nowrap;gap:8px;align-items:center;padding:10px 8px 6px;margin-top:6px;border-top:1px solid var(--cn-border);}
5354
+ .cn-root .beatrina-docsw-new{position:relative;display:inline-flex;}
5355
+ .cn-root .beatrina-docsw-drop button.beatrina-docsw-fbtn{
5356
+ width:auto;flex:0 0 auto;display:inline-flex;align-items:center;gap:7px;height:28px;min-height:28px;padding:0 11px 0 9px;
5357
+ border:1px solid var(--cn-border);border-radius:7px;background:var(--cn-surface);color:var(--cn-text);
5358
+ font:600 12px var(--cn-font);cursor:pointer;
5359
+ }
5360
+ .cn-root .beatrina-docsw-drop button.beatrina-docsw-fbtn svg{width:14px;height:14px;flex:0 0 auto;color:var(--cn-text-dim);}
5361
+ .cn-root .beatrina-docsw-drop button.beatrina-docsw-fbtn:not(:disabled):hover{background:var(--cn-control-bg);}
5362
+ .cn-root .beatrina-docsw-drop button.beatrina-docsw-fbtn:disabled{opacity:.45;cursor:default;}
5363
+ .cn-root .beatrina-docsw-drop .beatrina-docsw-fbtn .beatrina-menu-label{overflow:visible;}
5364
+ .cn-root .beatrina-docsw-drop .beatrina-docsw-fbtn .beatrina-docsw-caret{font-size:9px;color:var(--cn-text-dim);}
5365
+ .cn-root .beatrina-docsw-newmenu{
5366
+ position:absolute;left:0;bottom:calc(100% + 6px);display:flex;flex-direction:column;gap:2px;min-width:200px;padding:6px;
5367
+ background:var(--cn-bg,#fff);border:1px solid var(--cn-border);border-radius:9px;box-shadow:0 8px 24px rgba(0,0,0,.14);z-index:3;
5368
+ }
5369
+ .cn-root .beatrina-docsw-newmenu[hidden]{display:none;}
5370
+ .cn-root .beatrina-docsw-drop .beatrina-docsw-newmenu button.beatrina-docsw-fbtn{border-color:transparent;background:transparent;width:100%;justify-content:flex-start;font-weight:500;}
5371
+ .cn-root .beatrina-docsw-drop .beatrina-docsw-newmenu button.beatrina-docsw-fbtn:not(:disabled):hover{background:var(--cn-control-bg);}
5372
+ .cn-root .beatrina-docsw-drop button.beatrina-docsw-fbtn.is-close{margin-left:auto;color:#c5221f;border-color:#e5b4b0;}
5373
+ .cn-root .beatrina-docsw-drop button.beatrina-docsw-fbtn.is-close .beatrina-docsw-x-glyph{font-size:16px;line-height:1;}
5374
+ .cn-root .beatrina-docsw-drop button.beatrina-docsw-fbtn.is-close:not(:disabled):hover{background:#fce8e6;}
5210
5375
  .cn-root .beatrina-docsw-recent{display:flex !important;align-items:center;gap:8px;color:var(--cn-text-dim);}
5376
+ .cn-root .beatrina-docsw-facts{flex:0 0 auto;font:10.5px var(--cn-font);color:var(--cn-text);white-space:nowrap;}
5377
+ /* Specific enough to beat the drop's generic button rule (which stretched it to 434px, measured). */
5378
+ .cn-root .beatrina-docsw-drop button.beatrina-docsw-clear{
5379
+ margin-left:auto;width:auto;flex:0 0 auto;display:inline-flex;align-items:center;height:22px;min-height:22px;padding:0 9px;
5380
+ border:1px solid var(--cn-border);border-radius:6px;background:var(--cn-surface);
5381
+ color:var(--cn-text-dim);font:600 10.5px var(--cn-font);letter-spacing:0;text-transform:none;cursor:pointer;
5382
+ }
5383
+ .cn-root .beatrina-docsw-drop button.beatrina-docsw-clear:not(:disabled):hover{color:#c5221f;border-color:#c5221f;background:#fce8e6;}
5384
+ .cn-root .beatrina-docsw-drop button.beatrina-docsw-clear:disabled{opacity:.4;cursor:default;}
5385
+ .cn-root .beatrina-docsw-forget{
5386
+ flex:0 0 auto;width:22px;height:22px;display:inline-flex;align-items:center;justify-content:center;
5387
+ border:1px solid transparent;border-radius:6px;color:var(--cn-text-dim);font-size:14px;line-height:1;
5388
+ }
5389
+ .cn-root .beatrina-docsw-recent:hover .beatrina-docsw-forget{border-color:var(--cn-border);}
5390
+ .cn-root .beatrina-docsw-forget:hover{color:#c5221f;border-color:#c5221f !important;background:#fce8e6;}
5211
5391
  .cn-root .beatrina-docsw-recent .beatrina-docsw-label{font:500 11.5px var(--cn-mono);}
5212
5392
  /* A recent row: the name over its path, when it was at the right. */
5213
5393
  .cn-root .beatrina-docsw-recent-text{display:flex;flex-direction:column;gap:1px;min-width:0;flex:1 1 auto;}
5214
5394
  .cn-root .beatrina-docsw-recent .beatrina-docsw-path{grid-column:auto;grid-row:auto;}
5215
- .cn-root .beatrina-docsw-when{flex:0 0 auto;font:10.5px var(--cn-font);color:var(--cn-text-dim);white-space:nowrap;}
5395
+ .cn-root .beatrina-docsw-when{flex:0 0 auto;font:10.5px var(--cn-font);color:var(--cn-text);white-space:nowrap;}
5216
5396
  .cn-root .beatrina-docsw-note{font:10.5px var(--cn-font);color:var(--cn-text-dim);padding:8px 8px 2px;}
5217
5397
 
5218
5398
  .cn-root .hdr-row-id .beatrina-kernel{order:8;}
@@ -5485,11 +5665,18 @@ var BEATRINA_CSS = `
5485
5665
  The Code menu read this way since 7.5 and the strips beside it did not;
5486
5666
  the owner called that out (2026-09-03), so now File \u25BE through Help \u25BE all
5487
5667
  do. Width is derived from the column count so every panel shares one
5488
- recipe. */
5668
+ recipe.
5669
+
5670
+ ONE column width for every panel, wide enough for the longest row a menu
5671
+ carries \u2014 icon, a sentence of a label, a shortcut. At 226 px, "Send
5672
+ Selection or Line to C\u2026", "Restart R and Run All Chunk\u2026", "Convert again
5673
+ from Markdo\u2026" were all cut with room to spare in the window (FINDINGS
5674
+ 2026-09-22 B11); 272 px holds the longest with its key, measured. */
5675
+ .cn-root{--beatrina-menu-colw:272px;}
5489
5676
  .cn-root .beatrina-menu-drop.beatrina-menu-cols{
5490
5677
  columns:var(--beatrina-menu-cols,3);column-gap:16px;
5491
5678
  column-rule:1px solid var(--cn-border-soft);
5492
- width:min(calc(var(--beatrina-menu-cols,3) * 226px + 40px), calc(100vw - 24px));
5679
+ width:min(calc(var(--beatrina-menu-cols,3) * var(--beatrina-menu-colw) + 40px), calc(100vw - 24px));
5493
5680
  padding:10px 12px 6px;
5494
5681
  }
5495
5682
  .cn-root .beatrina-menu-cols .beatrina-menu-group{
@@ -5705,6 +5892,17 @@ var BEATRINA_CSS = `
5705
5892
  .cn-root[data-app-theme="plain"] .beatrina-ai-capability[data-intent="plan"]{
5706
5893
  background:var(--cn-accent-soft);color:var(--cn-text);
5707
5894
  }
5895
+ /* Code \u25BE is an OUTLINED pill here, like Quick write and Pin beside it: a
5896
+ graphite fill read as a black block (owner, 2026-09-22: "overly 3d"). */
5897
+ .cn-root:is([data-app-theme="flat"],[data-app-theme="plain"]) .beatrina-ai-capability-one,
5898
+ .cn-root:is([data-app-theme="flat"],[data-app-theme="plain"]) .beatrina-ai-capability-more{
5899
+ background:transparent;color:var(--cn-text);border-color:var(--cn-border-strong,#c9ced6);box-shadow:none;font-weight:600;
5900
+ }
5901
+ .cn-root:is([data-app-theme="flat"],[data-app-theme="plain"]) .beatrina-ai-capability-more{border-left-color:var(--cn-border-strong,#c9ced6);}
5902
+ .cn-root:is([data-app-theme="flat"],[data-app-theme="plain"]) .beatrina-ai-capability-one:hover:not(:disabled),
5903
+ .cn-root:is([data-app-theme="flat"],[data-app-theme="plain"]) .beatrina-ai-capability-more:hover:not(:disabled){
5904
+ background:var(--cn-control-bg);color:var(--cn-text);
5905
+ }
5708
5906
  .cn-root[data-app-theme="plain"] .beatrina-chunk-name-input{border-color:var(--cn-border-strong);}
5709
5907
  /* Save is an ACTION, and its blue is a literal that predates the accent
5710
5908
  token \u2014 in a monochrome theme it follows the accent like every other
@@ -5946,7 +6144,8 @@ var BEATRINA_CSS = `
5946
6144
  destination is one toggle. All three controls exist in the DOM under every
5947
6145
  layout (lib/ai-code-button.js) \u2014 this is where the layout chooses. */
5948
6146
  .cn-root[data-chunk-layout="sheet"] .beatrina-ai-capability{display:none;}
5949
- .cn-root[data-chunk-layout="sheet"] .beatrina-ai-capability-one{display:inline-flex;align-items:center;gap:4px;}
6147
+ .cn-root[data-chunk-layout="sheet"] .beatrina-ai-capability-one,.cn-root[data-chunk-layout="sheet"] .beatrina-ai-capability-more,
6148
+ .cn-root[data-chunk-layout="sheet"] .beatrina-ai-capability-one:hover:not(:disabled),.cn-root[data-chunk-layout="sheet"] .beatrina-ai-capability-more:hover:not(:disabled){display:inline-flex;align-items:center;color:var(--cn-text-muted);background:transparent;font-weight:500;border-color:transparent;}
5950
6149
  .cn-root[data-chunk-layout="sheet"] .beatrina-ai-context-group{display:none;}
5951
6150
  .cn-root[data-chunk-layout="sheet"] .beatrina-ai-destination-button{display:none;}
5952
6151
  .cn-root[data-chunk-layout="sheet"] .beatrina-ai-destination-toggle{display:inline-flex;cursor:pointer;white-space:nowrap;font:500 var(--cn-fs-xs) var(--cn-font);}
@@ -7488,15 +7687,32 @@ var BEATRINA_CSS = `
7488
7687
  position:sticky;top:0;z-index:1;background:var(--cn-surface);
7489
7688
  border-bottom:1px solid var(--cn-border);padding:0;text-align:left;
7490
7689
  }
7690
+ /* The header's first line is TWO sibling buttons in one band: the name
7691
+ (select the column) and the arrow (sort). Siblings, not nested \u2014 a button
7692
+ inside a button is invalid and the inner one had no keyboard path (N13).
7693
+ The band carries the hover and selected paint so the pair reads as one
7694
+ header; the 10 px outer padding is split across the two so the name still
7695
+ starts on the same vertical as the distribution line below it. */
7696
+ .cn-root .beatrina-grid-head{display:flex;align-items:stretch;}
7491
7697
  .cn-root .beatrina-grid-sort{
7492
- display:flex;align-items:center;gap:4px;width:100%;padding:5px 10px;
7698
+ display:flex;align-items:center;gap:4px;flex:1 1 auto;min-width:0;padding:5px 4px 5px 10px;
7493
7699
  font:600 11px var(--cn-font);color:var(--cn-text-muted);cursor:pointer;
7494
7700
  background:transparent;border:0;white-space:nowrap;
7495
7701
  }
7496
7702
  .cn-root .beatrina-grid th.is-num .beatrina-grid-sort{justify-content:flex-end;}
7497
- .cn-root .beatrina-grid-sort:hover{color:var(--cn-accent-deep);background:var(--cn-accent-soft);}
7498
- .cn-root .beatrina-grid th.is-sorted .beatrina-grid-sort{color:var(--cn-accent-deep);}
7499
- .cn-root .beatrina-grid-arrow{font-size:9px;line-height:1;}
7703
+ .cn-root .beatrina-grid-head:hover{background:var(--cn-accent-soft);}
7704
+ .cn-root .beatrina-grid-head:hover .beatrina-grid-sort,
7705
+ .cn-root .beatrina-grid-head:hover .beatrina-grid-arrow{color:var(--cn-accent-deep);}
7706
+ .cn-root .beatrina-grid th.is-sorted .beatrina-grid-sort,
7707
+ .cn-root .beatrina-grid th.is-sorted .beatrina-grid-arrow{color:var(--cn-accent-deep);}
7708
+ .cn-root .beatrina-grid-arrow{
7709
+ flex:0 0 auto;padding:5px 10px 5px 4px;border:0;background:transparent;cursor:pointer;
7710
+ font:600 9px var(--cn-font);line-height:1;color:var(--cn-text-muted);
7711
+ }
7712
+ .cn-root .beatrina-grid-sort:focus-visible,
7713
+ .cn-root .beatrina-grid-arrow:focus-visible{
7714
+ outline:2px solid var(--cn-accent-deep);outline-offset:-2px;
7715
+ }
7500
7716
  .cn-root .beatrina-grid td{
7501
7717
  padding:3px 10px;border-bottom:1px solid var(--cn-border-soft);
7502
7718
  white-space:nowrap;max-width:28ch;overflow:hidden;text-overflow:ellipsis;
@@ -7527,12 +7743,15 @@ var BEATRINA_CSS = `
7527
7743
  .cn-root .beatrina-grid td.is-sel-bottom{--sel-b:var(--cn-accent-deep,#31537a);}
7528
7744
  .cn-root .beatrina-grid td.is-sel-left{--sel-l:var(--cn-accent-deep,#31537a);}
7529
7745
  .cn-root .beatrina-grid td.is-sel-right{--sel-r:var(--cn-accent-deep,#31537a);}
7530
- .cn-root .beatrina-grid th.is-selected .beatrina-grid-sort{
7531
- color:var(--cn-accent-deep);background:var(--cn-accent-soft);
7746
+ .cn-root .beatrina-grid th.is-selected .beatrina-grid-sort,
7747
+ .cn-root .beatrina-grid th.is-selected .beatrina-grid-arrow{color:var(--cn-accent-deep);}
7748
+ .cn-root .beatrina-grid th.is-selected .beatrina-grid-head{
7749
+ background:var(--cn-accent-soft);
7532
7750
  box-shadow:inset 0 -2px 0 var(--cn-accent-deep,#31537a);
7533
7751
  }
7534
7752
  .cn-root .beatrina-grid-arrow.is-idle{opacity:.45;}
7535
- .cn-root .beatrina-grid-sort:hover .beatrina-grid-arrow.is-idle{opacity:.9;}
7753
+ .cn-root .beatrina-grid-head:hover .beatrina-grid-arrow.is-idle,
7754
+ .cn-root .beatrina-grid-arrow.is-idle:focus-visible{opacity:.9;}
7536
7755
  .cn-root .beatrina-grid .beatrina-grid-idx{
7537
7756
  position:sticky;left:0;background:var(--cn-surface);color:var(--cn-text-dim);
7538
7757
  font-family:var(--cn-mono);font-size:10px;text-align:right;padding:3px 8px;
@@ -8806,6 +9025,22 @@ var BEATRINA_CSS = `
8806
9025
  color:var(--cn-text-dim);font:500 var(--cn-fs-sm,11px) var(--cn-mono);white-space:nowrap;background:transparent;border:0;box-shadow:none;padding:0;
8807
9026
  }
8808
9027
  .cn-root .beatrina-context-separator{height:1px;margin:4px 8px;background:var(--cn-border-soft);}
9028
+ /* An icon row (the writing page's B \xB7 I \xB7 U \xB7 <>): the formats as one line of
9029
+ square buttons at the head of the menu, the way a word processor's
9030
+ right-click leads with them. Lit = the selection already carries it. */
9031
+ .cn-root .beatrina-context-iconrow{display:flex;gap:2px;padding:2px 6px;}
9032
+ .cn-root .beatrina-context-iconbtn{
9033
+ display:inline-flex;align-items:center;justify-content:center;width:32px;height:30px;padding:0;
9034
+ border:0;border-radius:var(--cn-radius-sm,6px);background:transparent;color:var(--cn-text-muted);cursor:pointer;
9035
+ }
9036
+ .cn-root .beatrina-context-iconbtn svg{display:block;width:16px;height:16px;}
9037
+ .cn-root .beatrina-context-iconbtn.is-word{width:auto;padding:0 9px;font:600 var(--cn-fs,13px)/1 var(--cn-font);}
9038
+ .cn-root .beatrina-context-iconbtn:hover,.cn-root .beatrina-context-iconbtn:focus{
9039
+ outline:0;background:color-mix(in srgb,var(--cn-accent) 10%,var(--cn-surface));color:var(--cn-accent-deep);
9040
+ }
9041
+ .cn-root .beatrina-context-iconbtn:focus-visible{outline:2px solid var(--cn-accent);outline-offset:-2px;}
9042
+ .cn-root .beatrina-context-iconbtn.is-on{background:color-mix(in srgb,var(--cn-accent) 16%,var(--cn-surface));color:var(--cn-accent-deep);}
9043
+ .cn-root .beatrina-context-iconbtn:disabled{opacity:.38;cursor:default;background:transparent;}
8809
9044
  /* A group row opens a fly-out beside the menu (Beatrina AI on the selection). The
8810
9045
  caret is the only sign; the row itself reads like every other item. */
8811
9046
  .cn-root .beatrina-context-item.is-submenu.is-open{
@@ -10431,6 +10666,14 @@ var BEATRINA_CSS = `
10431
10666
  @media(max-width:760px){
10432
10667
  .cn-root.has-ai-left{padding-left:0;}
10433
10668
  .cn-root.has-ai-right{padding-right:0;}
10669
+ /* \u2026and so a docked pane is a SHEET over the whole window, not a 400 px
10670
+ slab over the right half of the words. Reserving its width at this size
10671
+ would leave a column of ~260 px; overlaying half the text left every line
10672
+ cut off under it (journey windows, 720 px, 2026-09-24). One or the other
10673
+ is on screen; \xD7 gives the document back. !important beats the inline
10674
+ left/right/width ai-shell.js writes for the wide layout. */
10675
+ .cn-root .beatrina-ai-panel.is-side{left:0 !important;right:0 !important;width:auto !important;}
10676
+ .cn-root .beatrina-ai-panel.is-side .beatrina-ai-sedge{display:none;}
10434
10677
  }
10435
10678
 
10436
10679
  .cn-root .beatrina-aiw-convbtn{flex:0 0 auto;width:24px;height:24px;
@@ -11167,13 +11410,16 @@ body:has(.cn-root[data-beatrina-mode="writing"] > .beatrina-writing-bar){--beatr
11167
11410
  .cn-root[data-beatrina-mode="latex"] main > .beatrina-mtab-page{margin-bottom:0;}
11168
11411
  .cn-root[data-beatrina-mode="latex"] > .cn-footer{display:none;}
11169
11412
 
11170
- /* What goes away. The tab strip goes because the workbench IS the tab; the
11413
+ /* What goes away. The tab strip goes because the workbench IS the tab \u2014 while
11414
+ a .tex is in front; with Jobs, a data view or the console in front
11415
+ (data-latex-front="other", stamped by src/notebook-entry.js) it stays, or
11416
+ there is no visible way back (owner, 2026-09-22). The
11171
11417
  status line and the working-directory row go because the rail says where
11172
11418
  you are; the notebook's own add-strip and width control are answering
11173
11419
  questions this mode does not ask. The kernel badge and the session plug
11174
11420
  STAY \u2014 unlike Writing mode, R is live here and a reader has to be able to
11175
11421
  see that it is, because Compile is about to need it. */
11176
- .cn-root[data-beatrina-mode="latex"] .beatrina-mtabs,
11422
+ .cn-root[data-beatrina-mode="latex"]:not([data-latex-front="other"]) .beatrina-mtabs,
11177
11423
  .cn-root[data-beatrina-mode="latex"] .beatrina-status,
11178
11424
  .cn-root[data-beatrina-mode="latex"] .beatrina-wd,
11179
11425
  .cn-root[data-beatrina-mode="latex"] .beatrina-addstrip,
@@ -11368,6 +11614,10 @@ body:has(.cn-root[data-beatrina-mode="writing"] > .beatrina-writing-bar){--beatr
11368
11614
  .cn-root .beatrina-latex-rail-problems{flex:1 1 auto;min-height:0;overflow:auto;}
11369
11615
  .cn-root .beatrina-latex-rail-problems .beatrina-problems{padding:4px 0;}
11370
11616
  .cn-root .beatrina-latex-rail-problems .beatrina-problem{padding:7px 10px;}
11617
+ /* The Log view: the compiler's lines, as the console draws them. */
11618
+ .cn-root .beatrina-latex-rail-log{flex:1 1 auto;min-height:0;overflow:auto;font-size:var(--cn-fs-xs,11px);}
11619
+ .cn-root .beatrina-latex-rail-log-head{padding:6px 10px;color:var(--cn-muted,#666);}
11620
+ .cn-root .beatrina-latex-rail-log .beatrina-latex-rail-pre{margin:0;padding:4px 10px;white-space:pre-wrap;word-break:break-word;}
11371
11621
 
11372
11622
  /* \u2500\u2500 the start panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
11373
11623
  What the workbench shows before there is a .tex in it: the notebook you
@@ -11564,7 +11814,7 @@ body:has(.cn-root[data-beatrina-mode="writing"] > .beatrina-writing-bar){--beatr
11564
11814
  box-shadow:0 -1px 0 var(--cn-border,#e3e3e3);
11565
11815
  }
11566
11816
  .beatrina-dock[hidden]{display:none;}
11567
- .beatrina-dock[data-mode="bottom"]{left:0;right:0;bottom:0;flex-direction:column;}
11817
+ .beatrina-dock[data-mode="bottom"]{left:var(--beatrina-ai-edge-left,0px);right:var(--beatrina-ai-edge-right,0px);bottom:0;flex-direction:column;}
11568
11818
  .beatrina-dock[data-mode="right"]{
11569
11819
  top:0;right:0;bottom:0;flex-direction:row;
11570
11820
  box-shadow:-1px 0 0 var(--cn-border,#e3e3e3);
@@ -14692,7 +14942,8 @@ ${out}</div>
14692
14942
  if (!ctx.rawUrls && !safeImageSrc(url)) {
14693
14943
  return `<span class="beatrina-md-badsrc">[image: ${alt || "unsupported source"}]</span>`;
14694
14944
  }
14695
- let out = `<img src="${attrValue(encodeHref(url))}" alt="${alt}"`;
14945
+ const local = ctx.localImages && /^(?![a-z][a-z0-9+.-]*:|\/\/|#)/i.test(url.trim());
14946
+ let out = local ? `<img data-local-src="${attrValue(url)}" alt="${alt}"` : `<img src="${attrValue(encodeHref(url))}" alt="${alt}"`;
14696
14947
  if (node2.title) out += ` title="${attrValue(node2.title)}"`;
14697
14948
  if (!ctx.spec) out += ' class="beatrina-md-img" loading="lazy"';
14698
14949
  return ctx.spec ? `${out} />` : `${out}>`;
@@ -14781,6 +15032,7 @@ function toHtml(tree, opts = {}) {
14781
15032
  // survived a JSON round-trip or a parsed query, does not open it.
14782
15033
  rawHtml: opts.allowDangerousHtml === true,
14783
15034
  rawUrls: opts.allowDangerousProtocol === true,
15035
+ localImages: opts.localImages === true,
14784
15036
  gfm: opts.gfm !== false,
14785
15037
  softBreak: typeof opts.softBreak === "string" ? opts.softBreak : "\n",
14786
15038
  state: opts.state || {},
@@ -18484,17 +18736,6 @@ function citeStyleOf(notebook) {
18484
18736
  };
18485
18737
  }
18486
18738
 
18487
- // lib/engine-labels.js
18488
- var KNOWN_ENGINES = Object.freeze([
18489
- Object.freeze({ key: "r", label: "R" }),
18490
- Object.freeze({ key: "python", label: "Python" }),
18491
- Object.freeze({ key: "js", label: "JavaScript" }),
18492
- Object.freeze({ key: "bash", label: "Bash" }),
18493
- Object.freeze({ key: "sql", label: "SQL" })
18494
- ]);
18495
- var BY_KEY = new Map(KNOWN_ENGINES.map((e) => [e.key, e.label]));
18496
- var AUTHORING_ENGINES = Object.freeze(KNOWN_ENGINES.map((e) => e.key));
18497
-
18498
18739
  // lib/doc-mode.js
18499
18740
  var MODES = Object.freeze(["development", "writing", "latex"]);
18500
18741
  var MODE_KEY = "beatrina-mode";
@@ -19458,6 +19699,10 @@ ${references}
19458
19699
  `;
19459
19700
  }
19460
19701
 
19702
+ // lib/chunk-meta.js
19703
+ init_engine_labels();
19704
+ init_engine_labels();
19705
+
19461
19706
  // src/r-kernel.js
19462
19707
  function normalizeTable(frame) {
19463
19708
  const rows = Array.isArray(frame.rows) ? frame.rows : frame.rows ? [frame.rows] : [];
@@ -19470,6 +19715,9 @@ function normalizeTable(frame) {
19470
19715
  rows,
19471
19716
  nrow: frame.nrow,
19472
19717
  ncol: frame.ncol,
19718
+ // The frame's own width, row names not counted (worker.R; FINDINGS
19719
+ // 2026-09-22 B10). Absent from an older kernel, and then the page says ncol.
19720
+ ...Number.isFinite(frame.dataCols) ? { dataCols: frame.dataCols } : {},
19473
19721
  truncated: Boolean(frame.truncated),
19474
19722
  shown: rows.length,
19475
19723
  // The name the frame was printed under, when the printed expression was a
@@ -143,8 +143,14 @@ export function freePort() {
143
143
  }
144
144
 
145
145
  /**
146
- * Start a supervisor on `port`, open a page socket, and wait for R's ready.
147
- * @returns {Promise<object>} the session: frames, until, text, run, ask, ready, startupMs, log, shutdown
146
+ * Start a supervisor on `port`, open a page socket, and wait for it to be ready.
147
+ *
148
+ * A host that speaks the handshake sends `host-ready` first (plans/
149
+ * getting_rid_of_R.md K2). When its R row says R is NOT coming — absent, or
150
+ * failed to start — there is no R `ready` to wait for, and the session is the
151
+ * HOST: `ready` stays null, `hostReady` is the frame, `noR` says why. An older
152
+ * host sends no host-ready and is waited on for R's ready, as before.
153
+ * @returns {Promise<object>} the session: frames, until, text, run, ask, ready, hostReady, noR, startupMs, log, shutdown
148
154
  */
149
155
  export async function startSession({ command, port, cwd, env = {}, readyMs = 90000, logFile = "" }) {
150
156
  const t0 = Date.now();
@@ -214,7 +220,7 @@ export async function startSession({ command, port, cwd, env = {}, readyMs = 900
214
220
  else { try { process.kill(child.pid, "SIGKILL"); } catch { /* gone */ } }
215
221
  }
216
222
  };
217
- const session = { child, port, announced, page: null, frames: [], ready: null, startupMs: null, log: () => hostLog, shutdown };
223
+ const session = { child, port, announced, page: null, frames: [], ready: null, hostReady: null, noR: "", startupMs: null, log: () => hostLog, shutdown };
218
224
  if (!announced) return session;
219
225
 
220
226
  const page = await openSocket(port, `http://127.0.0.1:${port}`);
@@ -250,7 +256,16 @@ export async function startSession({ command, port, cwd, env = {}, readyMs = 900
250
256
  const reply = await session.until((f) => f.id === id && f.type !== "stdout" && f.type !== "stderr", ms, from);
251
257
  return { reply, ms: Date.now() - t };
252
258
  };
253
- session.ready = await session.until((f) => f.type === "ready", readyMs);
259
+ // host-ready is the first frame on open, or never (an older host): a short wait decides.
260
+ session.hostReady = await session.until((f) => f.type === "host-ready" || f.type === "ready", 15000);
261
+ if (session.hostReady && session.hostReady.type === "ready") { session.ready = session.hostReady; session.hostReady = null; }
262
+ const rRow = session.hostReady && Array.isArray(session.hostReady.engine_status)
263
+ ? session.hostReady.engine_status.find((e) => e.engine === "r") : null;
264
+ if (session.hostReady && (!rRow || !rRow.available || rRow.error)) {
265
+ session.noR = (rRow && (rRow.error || rRow.detail)) || "this host has no R engine";
266
+ } else if (!session.ready) {
267
+ session.ready = await session.until((f) => f.type === "ready", readyMs);
268
+ }
254
269
  session.startupMs = Date.now() - t0;
255
270
  return session;
256
271
  }
@@ -1 +1 @@
1
- 0.9.43
1
+ 1.0.0