beatrina 0.9.43 → 0.9.83

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.
@@ -1629,10 +1629,18 @@ var BEATRINA_CSS = `
1629
1629
  .cn-root .beatrina-ai-capability{
1630
1630
  border:1px solid transparent;padding:3px 9px;
1631
1631
  }
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;}
1632
+ /* ONE entrance in every layout (2026-09-22): "Code \u25BE" \u2014 the word acts, the
1633
+ arrow holds Chat and Plan. The three capability buttons stay in the DOM,
1634
+ hidden; the destination toggle is still the sheet layout's alone. */
1635
+ .cn-root .beatrina-ai-capability{display:none;}
1636
+ .cn-root .beatrina-ai-destination-toggle{display:none;}
1637
+ .cn-root .beatrina-ai-capability-one,.cn-root .beatrina-ai-capability-more{
1638
+ display:inline-flex;align-items:center;color:#fff;background:var(--cn-accent,#4e79a7);border:1px solid transparent;
1639
+ }
1640
+ .cn-root .beatrina-ai-capability-one{font-weight:750;padding:3px 8px 3px 9px;border-radius:6px 0 0 6px;margin-right:0;}
1641
+ .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);}
1642
+ .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);}
1643
+ .cn-root .beatrina-ai-capability-more .beatrina-ai-quickwrite-caret{font-size:10px;opacity:.9;pointer-events:none;}
1636
1644
  .cn-root .beatrina-ai-capability[data-intent="ask"]{
1637
1645
  color:var(--cn-accent-deep,#3a6a9f);background:transparent;
1638
1646
  }
@@ -2327,6 +2335,12 @@ var BEATRINA_CSS = `
2327
2335
  background:var(--cn-control-bg);color:var(--cn-text);
2328
2336
  }
2329
2337
  .cn-root .beatrina-prompt-input:focus{outline:none;border-color:var(--cn-accent);box-shadow:var(--cn-focus-ring);}
2338
+ /* The multi-line form (Writing mode's Edit source): Markdown, so the mono
2339
+ face and the size a source is read at; resizable downward only. */
2340
+ .cn-root textarea.beatrina-prompt-input{
2341
+ font-family:var(--cn-mono);font-size:var(--cn-fs);line-height:1.5;
2342
+ min-width:min(560px, 80vw);resize:vertical;
2343
+ }
2330
2344
 
2331
2345
  /* \u2500\u2500 the prompt editor \u2014 the words, and the right to change them \u2500\u2500 */
2332
2346
  .cn-root .beatrina-modal-panel.is-prompts{width:min(860px,95vw);}
@@ -3370,6 +3384,11 @@ var BEATRINA_CSS = `
3370
3384
  color:rgba(255,255,255,.72);font:12px var(--cn-font);pointer-events:none;
3371
3385
  animation:beatrina-focus-in .26s ease;
3372
3386
  }
3387
+ /* A Text cell on its own: a LIGHT, quiet page around the prose (owner,
3388
+ 2026-09-22: "it is all dark"). The code chunk keeps the dark haze. */
3389
+ .cn-root.beatrina-focus-prose .beatrina-focus-haze{background:color-mix(in srgb, var(--cn-bg,#fff) 94%, transparent);}
3390
+ .cn-root.beatrina-focus-prose .beatrina-focus-note{color:var(--cn-text-dim,#666);}
3391
+ .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
3392
  /* Narrower than the notebook, and a WORKING surface rather than a card.
3374
3393
  660px with the chunk's own height left a three-line chunk as a small box
3375
3394
  adrift in a large dark screen (owner, 2026-09-11: "This is so small"). The
@@ -3721,6 +3740,65 @@ var BEATRINA_CSS = `
3721
3740
  }
3722
3741
  .cn-root .beatrina-source-view .beatrina-highlight .mdh-yaml{color:var(--cn-text);}
3723
3742
 
3743
+ /* \u2500\u2500 Source mode reads like a README on GitHub (owner, 2026-09-22: "the
3744
+ source mode is ugly and horrible, even reading a readme on github is
3745
+ 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
3746
+ Everything below is DECORATION of the same bytes: no glyph changes and no
3747
+ line moves, so the transparent textarea stays exactly over its colour.
3748
+
3749
+ A reading width. At Full page width a prose line ran 1,400 px; a README is
3750
+ read at about a thousand. Centred in whatever the page gives it. */
3751
+ .cn-root .beatrina-source-view{max-width:min(100%, 1080px);margin:0 auto;}
3752
+ /* Prose always wraps here, whatever the code-chunk wrap setting says
3753
+ (lib/source-view.js passes softWrap: "always" to the surface, which makes
3754
+ the gutter follow the wrapped rows). */
3755
+ .cn-root .beatrina-source-view textarea.beatrina-code,
3756
+ .cn-root .beatrina-source-view .beatrina-highlight{white-space:pre-wrap;overflow-wrap:break-word;}
3757
+ .cn-root .beatrina-source-view textarea.beatrina-code{overflow-x:hidden;}
3758
+ .cn-root .beatrina-source-view .beatrina-highlight > span{display:block;}
3759
+ .cn-root .beatrina-source-view .beatrina-code-wrap::after,
3760
+ .cn-root .beatrina-source-view .beatrina-code-wrap::before{content:none;}
3761
+ /* Code blocks and the front matter on a band (lib/qmd-language.js names
3762
+ each line's region). Widened by a negative margin and an equal padding, so
3763
+ the band reaches past the text while the text itself stays exactly where
3764
+ the textarea puts it and wraps at the same width. */
3765
+ .cn-root .beatrina-source-view{
3766
+ --src-code-bg:color-mix(in srgb, var(--cn-text) 5%, var(--ed-bg, var(--cn-surface)));
3767
+ --src-yaml-bg:color-mix(in srgb, var(--cn-text) 3%, var(--ed-bg, var(--cn-surface)));
3768
+ }
3769
+ .cn-root .beatrina-source-view .beatrina-highlight > span:is(.qmd-open, .qmd-code, .qmd-close),
3770
+ .cn-root .beatrina-source-view .beatrina-highlight > span.qmd-yaml{
3771
+ margin:0 -10px;padding:0 10px;background:var(--src-code-bg);
3772
+ }
3773
+ .cn-root .beatrina-source-view .beatrina-highlight > span.qmd-yaml{background:var(--src-yaml-bg);}
3774
+ /* A line is 13 \xD7 1.62 = 21.06 px tall, so neighbouring bands meet on a
3775
+ fraction of a pixel and the page shows through as a hairline seam. Each
3776
+ band line casts its own colour one pixel down to close it (not the last). */
3777
+ .cn-root .beatrina-source-view .beatrina-highlight > span:is(.qmd-open, .qmd-code){box-shadow:0 1px 0 var(--src-code-bg);}
3778
+ .cn-root .beatrina-source-view .beatrina-highlight > span.qmd-yaml:has(+ span.qmd-yaml){box-shadow:0 1px 0 var(--src-yaml-bg);}
3779
+ .cn-root .beatrina-source-view .beatrina-highlight > span.qmd-open{border-radius:6px 6px 0 0;}
3780
+ .cn-root .beatrina-source-view .beatrina-highlight > span.qmd-close{border-radius:0 0 6px 6px;}
3781
+ .cn-root .beatrina-source-view .beatrina-highlight > span.qmd-open + span.qmd-close{border-radius:6px;}
3782
+ /* Headings in the editor theme's function colour, so they lead in every
3783
+ theme \u2014 in Plain the accent is graphite and a heading read as bold text. */
3784
+ .cn-root .beatrina-source-view .beatrina-highlight .mdh-h{color:var(--rtok-fn, var(--cn-accent-deep));}
3785
+ /* Copy all above the text; Copy beside each block's opening fence
3786
+ (lib/source-copy.js). Quiet until pointed at; a done copy says so in words
3787
+ and in the Okabe-Ito green \u2014 never by colour alone. */
3788
+ .cn-root .beatrina-src-actions{display:flex;justify-content:flex-end;margin:0 0 6px;}
3789
+ .cn-root .beatrina-src-copies{position:absolute;inset:0;pointer-events:none;z-index:3;}
3790
+ .cn-root .beatrina-src-copy{
3791
+ display:inline-flex;align-items:center;gap:5px;height:22px;padding:0 8px;
3792
+ font:500 var(--cn-fs-xs,11px)/1 var(--cn-font);color:var(--cn-text-muted);
3793
+ background:var(--cn-surface);border:1px solid var(--cn-border);border-radius:6px;cursor:pointer;
3794
+ }
3795
+ .cn-root .beatrina-src-copies .beatrina-src-copy{position:absolute;right:4px;margin-top:-1px;pointer-events:auto;opacity:.62;}
3796
+ .cn-root .beatrina-src-copy svg{width:13px;height:13px;flex:none;}
3797
+ .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);}
3798
+ .cn-root .beatrina-src-copy:focus-visible{outline:2px solid var(--cn-accent);outline-offset:1px;}
3799
+ .cn-root .beatrina-src-copy.is-done{opacity:1;color:#009E73;border-color:#009E73;}
3800
+ @media print{.cn-root .beatrina-src-actions,.cn-root .beatrina-src-copies{display:none!important;}}
3801
+
3724
3802
  /* \u2500\u2500 result bar: fold / clear \u2500\u2500 */
3725
3803
  .cn-root .beatrina-output-bar{
3726
3804
  display:flex;align-items:center;gap:5px;margin:3px 0 0;
@@ -4471,10 +4549,10 @@ var BEATRINA_CSS = `
4471
4549
  /* The row's fixed groups leave the bar roughly (viewport \u2212 1000) px. It
4472
4550
  gives up its least essential pixels first, and every group it drops is
4473
4551
  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. */
4552
+ click, never a command. Order of surrender: the count, breathing room,
4553
+ the insert group, the blocks group. (Cite is a word with no glyph now, so
4554
+ it is never shrunk to one.) */
4476
4555
  @media (max-width:1640px){
4477
- .cn-root header .beatrina-md-bar .is-cite .beatrina-md-tool-label{display:none;}
4478
4556
  .cn-root header .beatrina-md-bar .beatrina-md-count{display:none;}
4479
4557
  }
4480
4558
  @media (max-width:1560px){
@@ -4493,7 +4571,7 @@ var BEATRINA_CSS = `
4493
4571
  .cn-root header .beatrina-md-bar[data-fold="3"] .beatrina-md-group.is-insert,
4494
4572
  .cn-root header .beatrina-md-bar[data-fold="3"] .beatrina-md-group.is-blocks,
4495
4573
  .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),
4574
+ .cn-root header .beatrina-md-bar[data-fold="4"] .beatrina-md-group:not(.is-text):not(.is-cite-lead),
4497
4575
  .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
4576
  .cn-root header .beatrina-md-bar[data-fold="5"] .beatrina-md-group{display:none;}
4499
4577
  /* At the last fold what is left (the \u22EE shelf and Done) is the bar's minimum:
@@ -4545,7 +4623,7 @@ var BEATRINA_CSS = `
4545
4623
  .cn-root .beatrina-md-morebar{
4546
4624
  display:block; /* .beatrina-pop is a column flexbox, and a flexbox has no CSS columns */
4547
4625
  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));
4626
+ width:min(calc(var(--beatrina-menu-cols,2) * var(--beatrina-menu-colw) + 30px), calc(100vw - 24px));
4549
4627
  max-height:calc(100vh - 100px);overflow-y:auto;padding:6px 8px 2px;
4550
4628
  }
4551
4629
  .cn-root .beatrina-md-pop-group{break-inside:avoid;-webkit-column-break-inside:avoid;margin:0 0 8px;}
@@ -4851,18 +4929,22 @@ var BEATRINA_CSS = `
4851
4929
  .cn-root .cell.beatrina-md-cell:focus-within .beatrina-cell-rail{
4852
4930
  pointer-events:auto;
4853
4931
  }
4932
+ /* A bit larger (owner, 2026-09-22): 24px buttons, 15px glyphs \u2014 the most the 32px gutter band holds. */
4854
4933
  .cn-root .beatrina-cell-rail-btn{
4855
4934
  position:relative;
4856
4935
  display:flex;align-items:center;justify-content:center;
4857
- width:21px;height:21px;padding:0;border:0;border-radius:6px;
4936
+ width:24px;height:24px;padding:0;border:0;border-radius:7px;
4858
4937
  background:transparent;cursor:pointer;
4859
- color:color-mix(in srgb, var(--cn-text) 48%, transparent);
4938
+ color:color-mix(in srgb, var(--cn-text) 55%, transparent);
4860
4939
  transition:color var(--cn-dur-1),background var(--cn-dur-1);
4861
4940
  }
4862
4941
  .cn-root .beatrina-cell-rail-btn svg{
4863
- width:14px;height:14px;
4942
+ width:15px;height:15px;
4864
4943
  transition:transform var(--cn-dur-1);
4865
4944
  }
4945
+ /* The lock, once on, stays visible: a locked cell says so without a hover. */
4946
+ .cn-root .cell.locked .beatrina-cell-rail{pointer-events:auto;}
4947
+ .cn-root .cell.locked .beatrina-cell-rail-btn[data-command="text.lock"]{color:var(--cn-accent-deep);opacity:1;}
4866
4948
  .cn-root .beatrina-cell-rail-btn:hover{
4867
4949
  background:color-mix(in srgb, var(--cn-accent) 13%, transparent);
4868
4950
  color:var(--cn-accent-deep);
@@ -5117,6 +5199,8 @@ var BEATRINA_CSS = `
5117
5199
  font:700 var(--cn-fs-sm,12.5px) var(--cn-font);border:1px solid #E69F00;background:#E69F00;color:#1a1300;}
5118
5200
  .cn-root .beatrina-tolatex:hover{filter:brightness(1.06);}
5119
5201
  .cn-root .beatrina-tolatex:focus-visible{outline:2px solid var(--beatrina-hdr-focus,#fff);outline-offset:2px;}
5202
+ /* "Open <stem>.tex": a file name can be long; the button keeps its row. */
5203
+ .cn-root .beatrina-tolatex.is-open{max-width:220px;overflow:hidden;text-overflow:ellipsis;}
5120
5204
  /* In LaTeX the same place SAYS where you are \u2014 green, a dot as well as the
5121
5205
  colour, and not a button any more. */
5122
5206
  .cn-root .beatrina-tolatex.is-latex{background:#009E73;border-color:#009E73;color:#fff;cursor:default;}
@@ -5168,10 +5252,13 @@ var BEATRINA_CSS = `
5168
5252
  Import Data \xB7 Save drop their words) makes the room \u2014 the document's
5169
5253
  name outranks three verbs that keep their icons. */
5170
5254
  .cn-root .hdr-row-id .beatrina-docsw{order:7;flex:0 1 auto;min-width:120px;position:relative;}
5255
+ /* A BUTTON you can see (owner, 2026-09-22: "it should be open documents and
5256
+ should be clearer"): the header's own button face, the words on it, not a
5257
+ faint pill with a glyph. */
5171
5258
  .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;
5259
+ display:inline-flex;align-items:center;gap:8px;height:30px;max-width:min(360px,100%);min-width:0;padding:0 12px 0 10px;
5260
+ border-radius:8px;border:1px solid var(--beatrina-hdr-line);background:var(--beatrina-hdr-wash);box-shadow:none;
5261
+ color:var(--beatrina-hdr-fg);font:600 var(--cn-fs-sm,12.5px) var(--cn-font);cursor:pointer;
5175
5262
  }
5176
5263
  .cn-root .hdr-row-id .beatrina-docsw-btn:hover{background:var(--beatrina-hdr-wash);}
5177
5264
  .cn-root .hdr-row-id .beatrina-docsw-btn[aria-expanded="true"]{background:var(--beatrina-hdr-wash-2);}
@@ -5181,38 +5268,102 @@ var BEATRINA_CSS = `
5181
5268
  }
5182
5269
  .cn-root .beatrina-docsw-kind.is-notebook{border-color:transparent;background:var(--beatrina-hdr-wash-2);color:var(--beatrina-hdr-fg);}
5183
5270
  .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;}
5271
+ .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
5272
  .cn-root .beatrina-docsw-count{font:500 var(--cn-fs-xs) var(--cn-font);color:var(--beatrina-hdr-fg-faint);white-space:nowrap;}
5185
5273
  .cn-root .beatrina-docsw-count:empty{display:none;}
5186
5274
  .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);}
5275
+ .cn-root .beatrina-docsw .hdr-export-drop{left:auto;right:0;width:540px;max-width:calc(100vw - 24px);}
5276
+ .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;}
5277
+ .cn-root .beatrina-docsw-drop .beatrina-docsw-head{display:flex;align-items:center;justify-content:space-between;gap:8px;white-space:nowrap;}
5278
+ .cn-root .beatrina-docsw-head-note{font:500 10.5px var(--cn-font);color:var(--cn-text-dim);text-transform:none;letter-spacing:0;}
5279
+ .cn-root .beatrina-docsw-list{display:flex;flex-direction:column;gap:0;padding:2px 0;}
5280
+ .cn-root .beatrina-docsw-list > * + *{border-top:1px solid var(--cn-border-soft,#eef1f5);}
5281
+ /* A drawn icon per kind, the kind's badge under it. */
5282
+ .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);}
5283
+ .cn-root .beatrina-docsw-icon svg{width:18px;height:18px;}
5284
+ .cn-root .beatrina-docsw-icon .beatrina-docsw-badge{min-width:0;height:14px;padding:0 4px;font-size:8.5px;}
5189
5285
  .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);
5286
+ display:grid;grid-template-columns:36px minmax(0,1fr) auto 30px;grid-template-rows:auto auto;
5287
+ align-items:center;column-gap:10px;row-gap:2px;text-align:left;width:100%;padding:8px 10px;margin:0;
5288
+ border:0;border-radius:8px;background:transparent;color:var(--cn-text);cursor:pointer;font:var(--cn-fs-sm) var(--cn-font);
5193
5289
  }
5194
5290
  .cn-root .beatrina-docsw-drop .beatrina-docsw-row:hover{background:var(--cn-control-bg);}
5195
5291
  .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);}
5292
+ /* The one on screen: a wash, a bar at its edge, and the words "on screen" \u2014 never colour alone. */
5293
+ .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);}
5294
+ /* Every cell PLACED, not auto-flowed: the path in row 2 of column 2 sat under
5295
+ the chip's would-be column, so auto-placement pushed the chip one column
5296
+ right and the \xD7 into an implicit fifth \u2014 measured 36px 374px 0 30px 28px. */
5297
+ .cn-root .beatrina-docsw-drop .beatrina-docsw-row > .beatrina-docsw-icon{grid-column:1;grid-row:1 / span 2;}
5298
+ .cn-root .beatrina-docsw-drop .beatrina-docsw-row > .beatrina-docsw-mode{grid-column:3;grid-row:1 / span 2;}
5299
+ .cn-root .beatrina-docsw-drop .beatrina-docsw-row > .beatrina-docsw-x{grid-column:4;grid-row:1 / span 2;}
5300
+ .cn-root .beatrina-docsw-main{grid-column:2;grid-row:1;display:inline-flex;align-items:center;gap:7px;min-width:0;}
5301
+ .cn-root .beatrina-docsw-here{font:500 10.5px var(--cn-font);color:var(--cn-accent);white-space:nowrap;}
5197
5302
  .cn-root .beatrina-docsw-dot{width:7px;height:7px;border-radius:50%;border:1px solid var(--cn-border);background:transparent;}
5198
5303
  .cn-root .beatrina-docsw-dot.is-dirty{background:#E69F00;border-color:#E69F00;}
5199
5304
  .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;}
5305
+ .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
5306
  .cn-root .beatrina-docsw-mode{
5202
5307
  font:600 9.5px var(--cn-font);letter-spacing:.06em;text-transform:uppercase;color:var(--cn-text-dim);
5203
5308
  border:1px solid var(--cn-border);border-radius:4px;padding:1px 4px;line-height:1.2;white-space:nowrap;
5204
5309
  }
5205
5310
  .cn-root .beatrina-docsw-mode:empty{visibility:hidden;}
5206
5311
  .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;}
5312
+ .cn-root .beatrina-docsw-mode.is-on{color:var(--cn-accent);border-color:var(--cn-accent);}
5313
+ /* A Close you can see: a bordered square on every row. */
5314
+ .cn-root .beatrina-docsw-x{
5315
+ width:28px;height:28px;display:inline-flex;align-items:center;justify-content:center;
5316
+ border:1px solid var(--cn-border);border-radius:7px;background:var(--cn-surface);
5317
+ color:var(--cn-text-dim);font-size:16px;line-height:1;text-align:center;
5318
+ }
5319
+ .cn-root .beatrina-docsw-x:hover{color:#c5221f;border-color:#c5221f;background:#fce8e6;}
5320
+ .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);}
5321
+ .cn-root .beatrina-docsw-sub{grid-column:2;grid-row:2;display:flex;align-items:baseline;gap:5px;min-width:0;}
5322
+ .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;}
5323
+ /* The footer: ONE row \u2014 New \u25BE \xB7 Open\u2026 on the left, Close apart on the right; an icon and a word each. */
5324
+ .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);}
5325
+ .cn-root .beatrina-docsw-new{position:relative;display:inline-flex;}
5326
+ .cn-root .beatrina-docsw-drop button.beatrina-docsw-fbtn{
5327
+ width:auto;flex:0 0 auto;display:inline-flex;align-items:center;gap:7px;height:28px;min-height:28px;padding:0 11px 0 9px;
5328
+ border:1px solid var(--cn-border);border-radius:7px;background:var(--cn-surface);color:var(--cn-text);
5329
+ font:600 12px var(--cn-font);cursor:pointer;
5330
+ }
5331
+ .cn-root .beatrina-docsw-drop button.beatrina-docsw-fbtn svg{width:14px;height:14px;flex:0 0 auto;color:var(--cn-text-dim);}
5332
+ .cn-root .beatrina-docsw-drop button.beatrina-docsw-fbtn:not(:disabled):hover{background:var(--cn-control-bg);}
5333
+ .cn-root .beatrina-docsw-drop button.beatrina-docsw-fbtn:disabled{opacity:.45;cursor:default;}
5334
+ .cn-root .beatrina-docsw-drop .beatrina-docsw-fbtn .beatrina-menu-label{overflow:visible;}
5335
+ .cn-root .beatrina-docsw-drop .beatrina-docsw-fbtn .beatrina-docsw-caret{font-size:9px;color:var(--cn-text-dim);}
5336
+ .cn-root .beatrina-docsw-newmenu{
5337
+ position:absolute;left:0;bottom:calc(100% + 6px);display:flex;flex-direction:column;gap:2px;min-width:200px;padding:6px;
5338
+ 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;
5339
+ }
5340
+ .cn-root .beatrina-docsw-newmenu[hidden]{display:none;}
5341
+ .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;}
5342
+ .cn-root .beatrina-docsw-drop .beatrina-docsw-newmenu button.beatrina-docsw-fbtn:not(:disabled):hover{background:var(--cn-control-bg);}
5343
+ .cn-root .beatrina-docsw-drop button.beatrina-docsw-fbtn.is-close{margin-left:auto;color:#c5221f;border-color:#e5b4b0;}
5344
+ .cn-root .beatrina-docsw-drop button.beatrina-docsw-fbtn.is-close .beatrina-docsw-x-glyph{font-size:16px;line-height:1;}
5345
+ .cn-root .beatrina-docsw-drop button.beatrina-docsw-fbtn.is-close:not(:disabled):hover{background:#fce8e6;}
5210
5346
  .cn-root .beatrina-docsw-recent{display:flex !important;align-items:center;gap:8px;color:var(--cn-text-dim);}
5347
+ .cn-root .beatrina-docsw-facts{flex:0 0 auto;font:10.5px var(--cn-font);color:var(--cn-text);white-space:nowrap;}
5348
+ /* Specific enough to beat the drop's generic button rule (which stretched it to 434px, measured). */
5349
+ .cn-root .beatrina-docsw-drop button.beatrina-docsw-clear{
5350
+ margin-left:auto;width:auto;flex:0 0 auto;display:inline-flex;align-items:center;height:22px;min-height:22px;padding:0 9px;
5351
+ border:1px solid var(--cn-border);border-radius:6px;background:var(--cn-surface);
5352
+ color:var(--cn-text-dim);font:600 10.5px var(--cn-font);letter-spacing:0;text-transform:none;cursor:pointer;
5353
+ }
5354
+ .cn-root .beatrina-docsw-drop button.beatrina-docsw-clear:not(:disabled):hover{color:#c5221f;border-color:#c5221f;background:#fce8e6;}
5355
+ .cn-root .beatrina-docsw-drop button.beatrina-docsw-clear:disabled{opacity:.4;cursor:default;}
5356
+ .cn-root .beatrina-docsw-forget{
5357
+ flex:0 0 auto;width:22px;height:22px;display:inline-flex;align-items:center;justify-content:center;
5358
+ border:1px solid transparent;border-radius:6px;color:var(--cn-text-dim);font-size:14px;line-height:1;
5359
+ }
5360
+ .cn-root .beatrina-docsw-recent:hover .beatrina-docsw-forget{border-color:var(--cn-border);}
5361
+ .cn-root .beatrina-docsw-forget:hover{color:#c5221f;border-color:#c5221f !important;background:#fce8e6;}
5211
5362
  .cn-root .beatrina-docsw-recent .beatrina-docsw-label{font:500 11.5px var(--cn-mono);}
5212
5363
  /* A recent row: the name over its path, when it was at the right. */
5213
5364
  .cn-root .beatrina-docsw-recent-text{display:flex;flex-direction:column;gap:1px;min-width:0;flex:1 1 auto;}
5214
5365
  .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;}
5366
+ .cn-root .beatrina-docsw-when{flex:0 0 auto;font:10.5px var(--cn-font);color:var(--cn-text);white-space:nowrap;}
5216
5367
  .cn-root .beatrina-docsw-note{font:10.5px var(--cn-font);color:var(--cn-text-dim);padding:8px 8px 2px;}
5217
5368
 
5218
5369
  .cn-root .hdr-row-id .beatrina-kernel{order:8;}
@@ -5485,11 +5636,18 @@ var BEATRINA_CSS = `
5485
5636
  The Code menu read this way since 7.5 and the strips beside it did not;
5486
5637
  the owner called that out (2026-09-03), so now File \u25BE through Help \u25BE all
5487
5638
  do. Width is derived from the column count so every panel shares one
5488
- recipe. */
5639
+ recipe.
5640
+
5641
+ ONE column width for every panel, wide enough for the longest row a menu
5642
+ carries \u2014 icon, a sentence of a label, a shortcut. At 226 px, "Send
5643
+ Selection or Line to C\u2026", "Restart R and Run All Chunk\u2026", "Convert again
5644
+ from Markdo\u2026" were all cut with room to spare in the window (FINDINGS
5645
+ 2026-09-22 B11); 272 px holds the longest with its key, measured. */
5646
+ .cn-root{--beatrina-menu-colw:272px;}
5489
5647
  .cn-root .beatrina-menu-drop.beatrina-menu-cols{
5490
5648
  columns:var(--beatrina-menu-cols,3);column-gap:16px;
5491
5649
  column-rule:1px solid var(--cn-border-soft);
5492
- width:min(calc(var(--beatrina-menu-cols,3) * 226px + 40px), calc(100vw - 24px));
5650
+ width:min(calc(var(--beatrina-menu-cols,3) * var(--beatrina-menu-colw) + 40px), calc(100vw - 24px));
5493
5651
  padding:10px 12px 6px;
5494
5652
  }
5495
5653
  .cn-root .beatrina-menu-cols .beatrina-menu-group{
@@ -5705,6 +5863,17 @@ var BEATRINA_CSS = `
5705
5863
  .cn-root[data-app-theme="plain"] .beatrina-ai-capability[data-intent="plan"]{
5706
5864
  background:var(--cn-accent-soft);color:var(--cn-text);
5707
5865
  }
5866
+ /* Code \u25BE is an OUTLINED pill here, like Quick write and Pin beside it: a
5867
+ graphite fill read as a black block (owner, 2026-09-22: "overly 3d"). */
5868
+ .cn-root:is([data-app-theme="flat"],[data-app-theme="plain"]) .beatrina-ai-capability-one,
5869
+ .cn-root:is([data-app-theme="flat"],[data-app-theme="plain"]) .beatrina-ai-capability-more{
5870
+ background:transparent;color:var(--cn-text);border-color:var(--cn-border-strong,#c9ced6);box-shadow:none;font-weight:600;
5871
+ }
5872
+ .cn-root:is([data-app-theme="flat"],[data-app-theme="plain"]) .beatrina-ai-capability-more{border-left-color:var(--cn-border-strong,#c9ced6);}
5873
+ .cn-root:is([data-app-theme="flat"],[data-app-theme="plain"]) .beatrina-ai-capability-one:hover:not(:disabled),
5874
+ .cn-root:is([data-app-theme="flat"],[data-app-theme="plain"]) .beatrina-ai-capability-more:hover:not(:disabled){
5875
+ background:var(--cn-control-bg);color:var(--cn-text);
5876
+ }
5708
5877
  .cn-root[data-app-theme="plain"] .beatrina-chunk-name-input{border-color:var(--cn-border-strong);}
5709
5878
  /* Save is an ACTION, and its blue is a literal that predates the accent
5710
5879
  token \u2014 in a monochrome theme it follows the accent like every other
@@ -5946,7 +6115,8 @@ var BEATRINA_CSS = `
5946
6115
  destination is one toggle. All three controls exist in the DOM under every
5947
6116
  layout (lib/ai-code-button.js) \u2014 this is where the layout chooses. */
5948
6117
  .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;}
6118
+ .cn-root[data-chunk-layout="sheet"] .beatrina-ai-capability-one,.cn-root[data-chunk-layout="sheet"] .beatrina-ai-capability-more,
6119
+ .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
6120
  .cn-root[data-chunk-layout="sheet"] .beatrina-ai-context-group{display:none;}
5951
6121
  .cn-root[data-chunk-layout="sheet"] .beatrina-ai-destination-button{display:none;}
5952
6122
  .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 +7658,32 @@ var BEATRINA_CSS = `
7488
7658
  position:sticky;top:0;z-index:1;background:var(--cn-surface);
7489
7659
  border-bottom:1px solid var(--cn-border);padding:0;text-align:left;
7490
7660
  }
7661
+ /* The header's first line is TWO sibling buttons in one band: the name
7662
+ (select the column) and the arrow (sort). Siblings, not nested \u2014 a button
7663
+ inside a button is invalid and the inner one had no keyboard path (N13).
7664
+ The band carries the hover and selected paint so the pair reads as one
7665
+ header; the 10 px outer padding is split across the two so the name still
7666
+ starts on the same vertical as the distribution line below it. */
7667
+ .cn-root .beatrina-grid-head{display:flex;align-items:stretch;}
7491
7668
  .cn-root .beatrina-grid-sort{
7492
- display:flex;align-items:center;gap:4px;width:100%;padding:5px 10px;
7669
+ display:flex;align-items:center;gap:4px;flex:1 1 auto;min-width:0;padding:5px 4px 5px 10px;
7493
7670
  font:600 11px var(--cn-font);color:var(--cn-text-muted);cursor:pointer;
7494
7671
  background:transparent;border:0;white-space:nowrap;
7495
7672
  }
7496
7673
  .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;}
7674
+ .cn-root .beatrina-grid-head:hover{background:var(--cn-accent-soft);}
7675
+ .cn-root .beatrina-grid-head:hover .beatrina-grid-sort,
7676
+ .cn-root .beatrina-grid-head:hover .beatrina-grid-arrow{color:var(--cn-accent-deep);}
7677
+ .cn-root .beatrina-grid th.is-sorted .beatrina-grid-sort,
7678
+ .cn-root .beatrina-grid th.is-sorted .beatrina-grid-arrow{color:var(--cn-accent-deep);}
7679
+ .cn-root .beatrina-grid-arrow{
7680
+ flex:0 0 auto;padding:5px 10px 5px 4px;border:0;background:transparent;cursor:pointer;
7681
+ font:600 9px var(--cn-font);line-height:1;color:var(--cn-text-muted);
7682
+ }
7683
+ .cn-root .beatrina-grid-sort:focus-visible,
7684
+ .cn-root .beatrina-grid-arrow:focus-visible{
7685
+ outline:2px solid var(--cn-accent-deep);outline-offset:-2px;
7686
+ }
7500
7687
  .cn-root .beatrina-grid td{
7501
7688
  padding:3px 10px;border-bottom:1px solid var(--cn-border-soft);
7502
7689
  white-space:nowrap;max-width:28ch;overflow:hidden;text-overflow:ellipsis;
@@ -7527,12 +7714,15 @@ var BEATRINA_CSS = `
7527
7714
  .cn-root .beatrina-grid td.is-sel-bottom{--sel-b:var(--cn-accent-deep,#31537a);}
7528
7715
  .cn-root .beatrina-grid td.is-sel-left{--sel-l:var(--cn-accent-deep,#31537a);}
7529
7716
  .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);
7717
+ .cn-root .beatrina-grid th.is-selected .beatrina-grid-sort,
7718
+ .cn-root .beatrina-grid th.is-selected .beatrina-grid-arrow{color:var(--cn-accent-deep);}
7719
+ .cn-root .beatrina-grid th.is-selected .beatrina-grid-head{
7720
+ background:var(--cn-accent-soft);
7532
7721
  box-shadow:inset 0 -2px 0 var(--cn-accent-deep,#31537a);
7533
7722
  }
7534
7723
  .cn-root .beatrina-grid-arrow.is-idle{opacity:.45;}
7535
- .cn-root .beatrina-grid-sort:hover .beatrina-grid-arrow.is-idle{opacity:.9;}
7724
+ .cn-root .beatrina-grid-head:hover .beatrina-grid-arrow.is-idle,
7725
+ .cn-root .beatrina-grid-arrow.is-idle:focus-visible{opacity:.9;}
7536
7726
  .cn-root .beatrina-grid .beatrina-grid-idx{
7537
7727
  position:sticky;left:0;background:var(--cn-surface);color:var(--cn-text-dim);
7538
7728
  font-family:var(--cn-mono);font-size:10px;text-align:right;padding:3px 8px;
@@ -8806,6 +8996,22 @@ var BEATRINA_CSS = `
8806
8996
  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
8997
  }
8808
8998
  .cn-root .beatrina-context-separator{height:1px;margin:4px 8px;background:var(--cn-border-soft);}
8999
+ /* An icon row (the writing page's B \xB7 I \xB7 U \xB7 <>): the formats as one line of
9000
+ square buttons at the head of the menu, the way a word processor's
9001
+ right-click leads with them. Lit = the selection already carries it. */
9002
+ .cn-root .beatrina-context-iconrow{display:flex;gap:2px;padding:2px 6px;}
9003
+ .cn-root .beatrina-context-iconbtn{
9004
+ display:inline-flex;align-items:center;justify-content:center;width:32px;height:30px;padding:0;
9005
+ border:0;border-radius:var(--cn-radius-sm,6px);background:transparent;color:var(--cn-text-muted);cursor:pointer;
9006
+ }
9007
+ .cn-root .beatrina-context-iconbtn svg{display:block;width:16px;height:16px;}
9008
+ .cn-root .beatrina-context-iconbtn.is-word{width:auto;padding:0 9px;font:600 var(--cn-fs,13px)/1 var(--cn-font);}
9009
+ .cn-root .beatrina-context-iconbtn:hover,.cn-root .beatrina-context-iconbtn:focus{
9010
+ outline:0;background:color-mix(in srgb,var(--cn-accent) 10%,var(--cn-surface));color:var(--cn-accent-deep);
9011
+ }
9012
+ .cn-root .beatrina-context-iconbtn:focus-visible{outline:2px solid var(--cn-accent);outline-offset:-2px;}
9013
+ .cn-root .beatrina-context-iconbtn.is-on{background:color-mix(in srgb,var(--cn-accent) 16%,var(--cn-surface));color:var(--cn-accent-deep);}
9014
+ .cn-root .beatrina-context-iconbtn:disabled{opacity:.38;cursor:default;background:transparent;}
8809
9015
  /* A group row opens a fly-out beside the menu (Beatrina AI on the selection). The
8810
9016
  caret is the only sign; the row itself reads like every other item. */
8811
9017
  .cn-root .beatrina-context-item.is-submenu.is-open{
@@ -11167,13 +11373,16 @@ body:has(.cn-root[data-beatrina-mode="writing"] > .beatrina-writing-bar){--beatr
11167
11373
  .cn-root[data-beatrina-mode="latex"] main > .beatrina-mtab-page{margin-bottom:0;}
11168
11374
  .cn-root[data-beatrina-mode="latex"] > .cn-footer{display:none;}
11169
11375
 
11170
- /* What goes away. The tab strip goes because the workbench IS the tab; the
11376
+ /* What goes away. The tab strip goes because the workbench IS the tab \u2014 while
11377
+ a .tex is in front; with Jobs, a data view or the console in front
11378
+ (data-latex-front="other", stamped by src/notebook-entry.js) it stays, or
11379
+ there is no visible way back (owner, 2026-09-22). The
11171
11380
  status line and the working-directory row go because the rail says where
11172
11381
  you are; the notebook's own add-strip and width control are answering
11173
11382
  questions this mode does not ask. The kernel badge and the session plug
11174
11383
  STAY \u2014 unlike Writing mode, R is live here and a reader has to be able to
11175
11384
  see that it is, because Compile is about to need it. */
11176
- .cn-root[data-beatrina-mode="latex"] .beatrina-mtabs,
11385
+ .cn-root[data-beatrina-mode="latex"]:not([data-latex-front="other"]) .beatrina-mtabs,
11177
11386
  .cn-root[data-beatrina-mode="latex"] .beatrina-status,
11178
11387
  .cn-root[data-beatrina-mode="latex"] .beatrina-wd,
11179
11388
  .cn-root[data-beatrina-mode="latex"] .beatrina-addstrip,
@@ -11368,6 +11577,10 @@ body:has(.cn-root[data-beatrina-mode="writing"] > .beatrina-writing-bar){--beatr
11368
11577
  .cn-root .beatrina-latex-rail-problems{flex:1 1 auto;min-height:0;overflow:auto;}
11369
11578
  .cn-root .beatrina-latex-rail-problems .beatrina-problems{padding:4px 0;}
11370
11579
  .cn-root .beatrina-latex-rail-problems .beatrina-problem{padding:7px 10px;}
11580
+ /* The Log view: the compiler's lines, as the console draws them. */
11581
+ .cn-root .beatrina-latex-rail-log{flex:1 1 auto;min-height:0;overflow:auto;font-size:var(--cn-fs-xs,11px);}
11582
+ .cn-root .beatrina-latex-rail-log-head{padding:6px 10px;color:var(--cn-muted,#666);}
11583
+ .cn-root .beatrina-latex-rail-log .beatrina-latex-rail-pre{margin:0;padding:4px 10px;white-space:pre-wrap;word-break:break-word;}
11371
11584
 
11372
11585
  /* \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
11586
  What the workbench shows before there is a .tex in it: the notebook you
@@ -19470,6 +19683,9 @@ function normalizeTable(frame) {
19470
19683
  rows,
19471
19684
  nrow: frame.nrow,
19472
19685
  ncol: frame.ncol,
19686
+ // The frame's own width, row names not counted (worker.R; FINDINGS
19687
+ // 2026-09-22 B10). Absent from an older kernel, and then the page says ncol.
19688
+ ...Number.isFinite(frame.dataCols) ? { dataCols: frame.dataCols } : {},
19473
19689
  truncated: Boolean(frame.truncated),
19474
19690
  shown: rows.length,
19475
19691
  // The name the frame was printed under, when the printed expression was a
@@ -1 +1 @@
1
- 0.9.43
1
+ 0.9.83
@@ -71,7 +71,14 @@ run_document_cell <- function(id, source, document, dims = NULL, srcname = NULL)
71
71
  if (identical(frame$type, "stdout") && !is.null(options$comment) && nzchar(options$comment)) {
72
72
  frame$text <- paste0(options$comment, " ", strsplit(frame$text, "\n", fixed = TRUE)[[1L]], collapse = "\n")
73
73
  }
74
- if (identical(frame$type, "plot") && save_figures) {
74
+ # A figure FILE is written only when the author asked for one by
75
+ # setting fig.path (a chunk option or the setup chunk's opts_chunk).
76
+ # knitr's own default, "figure/", is not a request: every plot of an
77
+ # ordinary run used to land as figure/<label>-1.png in the working
78
+ # directory, a folder nobody made (FINDINGS 2026-09-22 B17). The
79
+ # plot itself travels in the frame either way; nothing reads the file.
80
+ wants_file <- !identical(options$fig.path, KNIT_BASE$fig.path)
81
+ if (identical(frame$type, "plot") && save_figures && wants_file) {
75
82
  plot_n <<- plot_n + 1L
76
83
  suffix <- if (identical(frame$mime, "image/svg+xml")) ".svg" else ".png"
77
84
  target <- paste0(options$fig.path, options$label, "-", plot_n, suffix)
@@ -437,11 +437,12 @@ const TOOLS = [
437
437
  description:
438
438
  "Run one chunk in the user's live session (its own engine: R, or Python where the session runs it) and return its output "
439
439
  + "(stdout, messages, errors, table/plot summaries). The run is visible "
440
- + "in the notebook exactly as if the user pressed Run. REFUSED while the "
441
- + "document is in Writing mode — R is quiet there and nothing runs, not "
442
- + "even for an agent; the error says so. Do not retry: ask the user to "
443
- + "switch the document to Develop. Reading (notebook_read, chunk_read) and "
444
- + "authoring (chunk_insert, chunk_update) still work in Writing mode. "
440
+ + "in the notebook exactly as if the user pressed Run. If the document is in "
441
+ + "Writing mode the run first switches it to Develop — visibly, exactly as "
442
+ + "pressing the Develop segment — and the reply says so (`modeChanged: "
443
+ + "\"writing→development\"`); only when the page refuses that switch is the "
444
+ + "run refused, and the error says to ask the user. Reading (notebook_read, "
445
+ + "chunk_read) and authoring (chunk_insert, chunk_update) never change the mode. "
445
446
  + "A chunk with cache=TRUE and an unchanged source is RESTORED from knitr's "
446
447
  + "cache, not executed — the reply says so (`cached: true`) and none of the "
447
448
  + "chunk's side effects (setwd(), files written, seeds) happen; pass `force` "
package/kernel/worker.R CHANGED
@@ -166,6 +166,13 @@ MAX_VIEW_COLS <- 100L
166
166
  MAX_VIEW_BYTES <- 512L * 1024L
167
167
  MAX_VIEW_CELL_CHARS <- 512L
168
168
  MAX_VIEW_LABEL_CHARS <- 128L
169
+ # The viewer's page size, the same number as lib/panel.js's PAGE (pinned by
170
+ # spike/test-ide.R). View() pushes its first page unasked, and it
171
+ # used to take the `view` op's protocol default of 200, so the grid opened on
172
+ # "1–200 of N" and every page it asked for itself — next, search, clear — was
173
+ # 50 (N11, app-monkey soak 2026-09-22). The protocol default stays 200 for
174
+ # clients that name no limit; the push names the viewer's.
175
+ VIEWER_PAGE_ROWS <- 50L
169
176
  MAX_STRUCT_CHILDREN <- 200L
170
177
  MAX_COMPLETIONS <- 200L
171
178
 
@@ -2680,6 +2687,14 @@ emit_dataframe <- function(id, df, source = NULL) {
2680
2687
  # R whether names are automatic without materialising them; explicit names
2681
2688
  # are sliced from the raw attribute only AFTER the 10-row preview is taken.
2682
2689
  explicit_rownames <- .row_names_info(df, type = 1L) > 0L
2690
+ # head(df) and df[1:n, ] store their row names EXPLICITLY as the integers
2691
+ # 1..n — automatic in all but storage. Shown, they were a "rowname" column of
2692
+ # 1, 2, 3 and the header said 5 x 3 for a 5 x 2 frame (FINDINGS 2026-09-22
2693
+ # B10). Only integer names are tested, so a character vector is never built.
2694
+ if (explicit_rownames) {
2695
+ raw_names <- attr(df, "row.names", exact = TRUE)
2696
+ if (is.integer(raw_names) && identical(raw_names, seq_len(total_rows))) explicit_rownames <- FALSE
2697
+ }
2683
2698
  total_cols <- data_cols + as.integer(explicit_rownames)
2684
2699
  data_col_cap <- max(0L, preview_cols - as.integer(explicit_rownames))
2685
2700
  shown_cols <- if (data_cols && data_col_cap) {
@@ -2768,6 +2783,11 @@ emit_dataframe <- function(id, df, source = NULL) {
2768
2783
  columns = I(names(head_df)),
2769
2784
  types = I(source_types),
2770
2785
  nrow = total_rows, ncol = total_cols,
2786
+ # The frame's OWN width: row names are labels, not a variable, so the
2787
+ # "n x m" a reader sees is nrow x dataCols (mtcars is 32 x 11, not 12).
2788
+ # `ncol` keeps counting the displayed row-name column, which is what the
2789
+ # column pager and `truncated` measure against.
2790
+ dataCols = data_cols,
2771
2791
  shown = nrow(head_df), shownCols = ncol(head_df),
2772
2792
  truncated = total_rows > nrow(head_df) || total_cols > ncol(head_df),
2773
2793
  # jsonlite drops a NULL, so a frame with no name is byte-identical to what
@@ -3721,7 +3741,7 @@ beatrina_tools$View <- function(x, title = NULL) {
3721
3741
  # mtcars lives in the datasets package rather than the global environment.
3722
3742
  label <- if (!is.null(title)) title else deparse(substitute(x))
3723
3743
  assign(".beatrina_view", x, envir = globalenv())
3724
- emit_view("view-request", ".beatrina_view", label = label)
3744
+ emit_view("view-request", ".beatrina_view", label = label, limit = VIEWER_PAGE_ROWS)
3725
3745
  invisible(NULL)
3726
3746
  }
3727
3747
  attach(beatrina_tools, name = "beatrina:tools", warn.conflicts = FALSE)
@@ -6,8 +6,8 @@
6
6
  <key>CFBundleExecutable</key><string>Beatrina Menu</string>
7
7
  <key>CFBundleIdentifier</key><string>me.saqr.beatrina.menu</string>
8
8
  <key>CFBundlePackageType</key><string>APPL</string>
9
- <key>CFBundleShortVersionString</key><string>0.9.43</string>
10
- <key>CFBundleVersion</key><string>0.9.43</string>
9
+ <key>CFBundleShortVersionString</key><string>0.9.83</string>
10
+ <key>CFBundleVersion</key><string>0.9.83</string>
11
11
  <key>LSMinimumSystemVersion</key><string>13.0</string>
12
12
  <!-- Menu bar only: no Dock tile, no window, nothing to close by accident. -->
13
13
  <key>LSUIElement</key><true/>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beatrina",
3
- "version": "0.9.43",
3
+ "version": "0.9.83",
4
4
  "description": "Beatrina is a modern, powerful, feature-rich integrated development environment (IDE) for R, Python, and JavaScript.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -24,8 +24,8 @@
24
24
  "lib",
25
25
  "menu",
26
26
  "build-info.json",
27
- "beatrina_V0.9.43.html",
28
- "beatrina_V0.9.43.html.inputs.json",
27
+ "beatrina_V0.9.83.html",
28
+ "beatrina_V0.9.83.html.inputs.json",
29
29
  "README.md",
30
30
  "LICENSE",
31
31
  "NOTICES"