@shadow-garden/bapbong-ui 0.16.0 → 0.18.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.
package/dist/index.cjs CHANGED
@@ -33,11 +33,13 @@ __export(index_exports, {
33
33
  createFindDialog: () => createFindDialog,
34
34
  createSectionChip: () => createSectionChip,
35
35
  createSymbolDialog: () => createSymbolDialog,
36
+ createTableStylePanel: () => createTableStylePanel,
36
37
  defaultMenus: () => defaultMenus,
37
38
  defaultToolbarGroups: () => defaultToolbarGroups,
38
39
  equationGallery: () => equationGallery,
39
40
  equationPanel: () => equationPanel,
40
41
  equationPreviewSvg: () => equationPreviewSvg,
42
+ foldPlan: () => foldPlan,
41
43
  installScrollbars: () => installScrollbars,
42
44
  marginPresetPicker: () => marginPresetPicker,
43
45
  mountMenubar: () => mountMenubar,
@@ -78,6 +80,13 @@ function shortcutLabel(command, registries) {
78
80
  }
79
81
  return void 0;
80
82
  }
83
+ var TABS_CSS = `
84
+ .bb-tabs{display:inline-flex;align-self:flex-start;border:0.5px solid var(--bb-ui-control-border,var(--bb-ui-border,#d8d6cf));border-radius:6px;overflow:hidden}
85
+ .bb-tab{height:30px;padding:0 18px;border:0;border-right:0.5px solid var(--bb-ui-border,#e3e3e0);background:transparent;color:inherit;opacity:.65;font:inherit;font-size:13px;cursor:pointer}
86
+ .bb-tab:last-child{border-right:0}
87
+ .bb-tab:hover{background:var(--bb-ui-hover,#f1efe8);opacity:.9}
88
+ .bb-tab[aria-selected="true"]{background:var(--bb-ui-active-bg,#e6f1fb);color:var(--bb-ui-active-fg,#0c447c);opacity:1}
89
+ `;
81
90
  function injectStyle(id, css) {
82
91
  if (document.getElementById(id)) return;
83
92
  const el5 = document.createElement("style");
@@ -687,6 +696,7 @@ var STYLE3 = `
687
696
  .bb-toolbar{display:flex;gap:10px;align-items:center;flex-wrap:nowrap;overflow:hidden;padding:6px 8px;font-family:var(--bb-ui-font,system-ui,-apple-system,sans-serif);color:var(--bb-ui-fg,#2c2c2a);background:var(--bb-ui-control-bg,var(--bb-ui-bg,#fff));border-bottom:1px solid var(--bb-ui-border,#e3e3e0);box-sizing:border-box}
688
697
  .bb-toolbar *{box-sizing:border-box}
689
698
  .bb-toolbar-group{display:flex;gap:2px;flex:none}
699
+ .bb-toolbar-group[hidden]{display:none}
690
700
  .bb-toolbar-group+.bb-toolbar-group{padding-left:10px;border-left:1px solid var(--bb-ui-border,#e3e3e0)}
691
701
  .bb-toolbar-more{margin-left:auto;flex:none}
692
702
  .bb-toolbar-pop{position:absolute;z-index:1200;top:100%;left:0;right:0;margin-top:4px;display:flex;flex-wrap:wrap;gap:10px;row-gap:6px;align-items:center;padding:6px 8px;background:var(--bb-ui-menu-bg,#fff);-webkit-backdrop-filter:var(--bb-ui-pop-filter,none);backdrop-filter:var(--bb-ui-pop-filter,none);border:1px solid var(--bb-ui-pop-border,var(--bb-ui-border,#e3e3e0));border-radius:10px;box-shadow:0 8px 28px rgba(0,0,0,.16);font-family:var(--bb-ui-font,system-ui,-apple-system,sans-serif);color:var(--bb-ui-fg,#2c2c2a)}
@@ -695,6 +705,9 @@ var STYLE3 = `
695
705
  .bb-toolbar-btn:hover{background:var(--bb-ui-hover,#f1efe8)}
696
706
  .bb-toolbar-btn.is-active{background:var(--bb-ui-active-bg,#e6f1fb);color:var(--bb-ui-active-fg,#0c447c);border-color:var(--bb-ui-active-border,#b5d4f4)}
697
707
  .bb-toolbar-btn:disabled{opacity:.38;cursor:default}
708
+ .bb-toolbar-btn.bb-btn-accent{gap:5px;padding:0 9px;font-size:12.5px;white-space:nowrap;background:var(--bb-ui-active-bg,#e6f1fb);color:var(--bb-ui-active-fg,#0c447c);border-color:var(--bb-ui-active-border,#b5d4f4)}
709
+ .bb-toolbar-btn.bb-btn-accent:hover{background:var(--bb-ui-active-bg,#e6f1fb);border-color:var(--bb-ui-active-fg,#0c447c)}
710
+ .bb-btn-caret{font-size:9px;opacity:.7;line-height:1}
698
711
  .bb-toolbar-select{height:30px;border:1px solid var(--bb-ui-control-border,var(--bb-ui-border,#e3e3e0));border-radius:6px;background:var(--bb-ui-control-bg,var(--bb-ui-bg,#fff));color:inherit;font-family:inherit;font-size:13px;padding:0 6px;cursor:pointer}
699
712
  .bb-toolbar-select:hover{background:var(--bb-ui-hover,#f1efe8)}
700
713
  .bb-i-bold{font-weight:700}.bb-i-italic{font-style:italic}.bb-i-underline{text-decoration:underline}.bb-i-strike{text-decoration:line-through}
@@ -719,6 +732,25 @@ function defaultToolbarGroups(commands) {
719
732
  const rest = names.filter((n) => !n.startsWith("align-"));
720
733
  return [rest, aligns].filter((g2) => g2.length > 0);
721
734
  }
735
+ function foldPlan(candidates, avail, moreWidth, gap) {
736
+ const vis = candidates.filter((c2) => !c2.hidden);
737
+ const rowWidth = vis.reduce((sum, c2) => sum + c2.width, 0) + gap * Math.max(0, vis.length - 1);
738
+ const folded = /* @__PURE__ */ new Set();
739
+ if (rowWidth <= avail + 1) return folded;
740
+ const order = [
741
+ ...vis.filter((c2) => !c2.sticky).reverse(),
742
+ ...vis.filter((c2) => c2.sticky).reverse()
743
+ ];
744
+ const fitsRemaining = () => {
745
+ const rem = vis.filter((c2) => !folded.has(c2.idx));
746
+ return rem.reduce((sum, c2) => sum + c2.width, 0) + gap * rem.length + moreWidth <= avail + 1;
747
+ };
748
+ for (const c2 of order) {
749
+ if (fitsRemaining()) break;
750
+ folded.add(c2.idx);
751
+ }
752
+ return folded;
753
+ }
722
754
  function mountToolbar(host, editor, options = {}) {
723
755
  injectStyle("bb-ui-toolbar-styles", STYLE3);
724
756
  const items = { ...DEFAULT_ITEMS, ...options.items ?? {} };
@@ -729,17 +761,25 @@ function mountToolbar(host, editor, options = {}) {
729
761
  root.className = "bb-toolbar";
730
762
  root.setAttribute("role", "toolbar");
731
763
  const buttons = [];
764
+ const groupList = [];
765
+ const conditionalGroups = [];
732
766
  const selects = [];
733
767
  const colors = [];
734
768
  const splits = [];
735
769
  let latest = null;
736
770
  for (const group of groups) {
737
- const entries = group.filter(
771
+ const spec = Array.isArray(group) ? { entries: group } : group;
772
+ const entries = spec.entries.filter(
738
773
  (e) => typeof e !== "string" || editor.commands.has(e)
739
774
  );
740
775
  if (entries.length === 0) continue;
741
776
  const groupEl = document.createElement("div");
742
777
  groupEl.className = "bb-toolbar-group";
778
+ groupList.push({ el: groupEl, sticky: !!spec.sticky, lastWidth: 0 });
779
+ if (spec.visible) {
780
+ conditionalGroups.push({ el: groupEl, visible: spec.visible });
781
+ groupEl.hidden = true;
782
+ }
743
783
  for (const entry of entries) {
744
784
  if (typeof entry !== "string" && entry.kind === "select") {
745
785
  const sel = document.createElement("select");
@@ -765,12 +805,24 @@ function mountToolbar(host, editor, options = {}) {
765
805
  if (typeof entry !== "string" && entry.kind === "button") {
766
806
  const btn2 = document.createElement("button");
767
807
  btn2.type = "button";
768
- btn2.className = "bb-toolbar-btn";
808
+ btn2.className = "bb-toolbar-btn" + (entry.accent ? " bb-btn-accent" : "");
769
809
  btn2.title = entry.title;
770
810
  btn2.setAttribute("aria-label", entry.title);
771
811
  btn2.setAttribute("aria-haspopup", "dialog");
772
812
  if (entry.svg) btn2.innerHTML = entry.svg;
773
813
  else btn2.textContent = entry.label ?? entry.title;
814
+ if (entry.text) {
815
+ const span = document.createElement("span");
816
+ span.textContent = entry.text;
817
+ btn2.appendChild(span);
818
+ }
819
+ if (entry.caret) {
820
+ const caret = document.createElement("span");
821
+ caret.className = "bb-btn-caret";
822
+ caret.setAttribute("aria-hidden", "true");
823
+ caret.textContent = "\u25BE";
824
+ btn2.appendChild(caret);
825
+ }
774
826
  btn2.addEventListener("mousedown", (e) => e.preventDefault());
775
827
  btn2.addEventListener("click", () => entry.onClick(btn2));
776
828
  groupEl.appendChild(btn2);
@@ -931,19 +983,52 @@ function mountToolbar(host, editor, options = {}) {
931
983
  pop.hidden = !pop.hidden;
932
984
  moreBtn.setAttribute("aria-expanded", String(!pop.hidden));
933
985
  });
934
- const fits = () => root.scrollWidth <= root.clientWidth + 1;
986
+ let gapPx = -1;
987
+ let moreW = -1;
988
+ const anchorIn = (container, idx) => {
989
+ for (let j = idx + 1; j < groupList.length; j++)
990
+ if (groupList[j].el.parentElement === container) return groupList[j].el;
991
+ return container === root ? moreBtn : null;
992
+ };
935
993
  const layout = () => {
936
- while (pop.firstChild) root.insertBefore(pop.firstChild, moreBtn);
937
- moreBtn.style.display = "none";
938
- closePop();
939
- wrap.querySelectorAll(".bb-split-pop").forEach((p) => p.hidden = true);
940
- if (fits()) return;
941
- moreBtn.style.display = "";
942
- const groupEls = Array.from(root.children).filter(
943
- (el5) => el5.classList.contains("bb-toolbar-group")
944
- );
945
- for (let i = groupEls.length - 1; i >= 0 && !fits(); i--) {
946
- pop.insertBefore(groupEls[i], pop.firstChild);
994
+ if (gapPx < 0)
995
+ gapPx = Number.parseFloat(getComputedStyle(root).columnGap) || 0;
996
+ if (moreW < 0) {
997
+ moreBtn.style.visibility = "hidden";
998
+ const prev = moreBtn.style.display;
999
+ moreBtn.style.display = "";
1000
+ moreW = moreBtn.offsetWidth;
1001
+ moreBtn.style.display = prev;
1002
+ moreBtn.style.visibility = "";
1003
+ }
1004
+ const avail = root.clientWidth;
1005
+ const candidates = groupList.map((g2, idx) => {
1006
+ const w = g2.el.offsetWidth;
1007
+ if (w > 0) g2.lastWidth = w;
1008
+ return {
1009
+ idx,
1010
+ width: g2.lastWidth,
1011
+ sticky: g2.sticky,
1012
+ hidden: g2.el.hidden
1013
+ };
1014
+ });
1015
+ const folded = foldPlan(candidates, avail, moreW, gapPx);
1016
+ let moved = false;
1017
+ groupList.forEach((g2, idx) => {
1018
+ const inPop = g2.el.parentElement === pop;
1019
+ const should = folded.has(idx);
1020
+ if (inPop === should) return;
1021
+ moved = true;
1022
+ if (should) pop.insertBefore(g2.el, anchorIn(pop, idx));
1023
+ else root.insertBefore(g2.el, anchorIn(root, idx));
1024
+ });
1025
+ const showMore = folded.size > 0;
1026
+ if (moreBtn.style.display !== "none" !== showMore)
1027
+ moreBtn.style.display = showMore ? "" : "none";
1028
+ if (!showMore) closePop();
1029
+ if (moved) {
1030
+ closePop();
1031
+ wrap.querySelectorAll(".bb-split-pop").forEach((p) => p.hidden = true);
947
1032
  }
948
1033
  };
949
1034
  let ro = null;
@@ -955,6 +1040,15 @@ function mountToolbar(host, editor, options = {}) {
955
1040
  layout();
956
1041
  const refresh = (state) => {
957
1042
  latest = state;
1043
+ let visibilityChanged = false;
1044
+ for (const g2 of conditionalGroups) {
1045
+ const show = g2.visible(state);
1046
+ if (show === g2.el.hidden) {
1047
+ g2.el.hidden = !show;
1048
+ visibilityChanged = true;
1049
+ }
1050
+ }
1051
+ if (visibilityChanged) layout();
958
1052
  for (const { name, el: el5 } of buttons) {
959
1053
  const cmd = editor.commands.get(name);
960
1054
  if (!cmd) continue;
@@ -2222,17 +2316,10 @@ for (const [name, ch] of Object.entries(import_bapbong_contracts3.MATH_AUTOCORRE
2222
2316
  if (!SHORTHAND.has(ch)) SHORTHAND.set(ch, `\\${name}`);
2223
2317
  var PREVIEW_PT2 = 10;
2224
2318
  var STYLE10 = `
2225
- .bb-eqp{width:352px;display:flex;flex-direction:column;background:var(--bb-ui-bg,#fff);border:0.5px solid var(--bb-ui-border,#e3e3e0);border-radius:10px;box-shadow:0 4px 16px rgba(0,0,0,.13);user-select:none;overflow:hidden}
2226
- .bb-eqp-tabs{display:flex;align-items:flex-end;gap:2px;padding:5px 5px 0;border-bottom:0.5px solid var(--bb-ui-border,#e3e3e0)}
2227
- .bb-eqp.is-collapsed .bb-eqp-tabs{border-bottom:0}
2228
- .bb-eqp-tab{padding:6px 11px;border:0;border-radius:6px 6px 0 0;background:transparent;color:inherit;font:inherit;font-size:13px;opacity:.65;cursor:pointer}
2229
- .bb-eqp-tab:hover{background:var(--bb-ui-hover,#f1efe8)}
2230
- .bb-eqp-tab[aria-selected="true"]{opacity:1;background:var(--bb-ui-sel,#e6f1fb);color:var(--bb-ui-sel-fg,#0c447c)}
2231
- .bb-eqp-fold{margin-left:auto;margin-bottom:5px;width:24px;height:24px;display:flex;align-items:center;justify-content:center;padding:0;border:0;border-radius:6px;background:transparent;color:inherit;opacity:.5;cursor:pointer}
2232
- .bb-eqp-fold:hover{background:var(--bb-ui-hover,#f1efe8);opacity:.9}
2319
+ .bb-eqp{width:352px;display:flex;flex-direction:column;background:var(--bb-ui-menu-bg,#fff);-webkit-backdrop-filter:var(--bb-ui-pop-filter,none);backdrop-filter:var(--bb-ui-pop-filter,none);border:1px solid var(--bb-ui-pop-border,var(--bb-ui-border,#e3e3e0));border-radius:10px;box-shadow:0 8px 28px rgba(0,0,0,.14);user-select:none;overflow:hidden}
2320
+ .bb-eqp-tabs{display:flex;padding:8px 8px 0}
2233
2321
  .bb-eqp-body{padding:8px}
2234
- .bb-eqp.is-collapsed .bb-eqp-body{display:none}
2235
- .bb-eqp-set{width:100%;height:30px;margin-bottom:7px;font:inherit;font-size:13px}
2322
+ .bb-eqp-set{width:100%;height:30px;margin-bottom:7px;border:0.5px solid var(--bb-ui-control-border,var(--bb-ui-border,#d8d6cf));border-radius:6px;background:var(--bb-ui-control-bg,#fff);color:inherit;font:inherit;font-size:13px;padding:0 8px}
2236
2323
  .bb-eqp-grid{display:grid;grid-template-columns:repeat(10,1fr);gap:1px;max-height:190px;overflow-y:auto}
2237
2324
  .bb-eqp-sym{padding:5px 0;border:0;border-radius:5px;background:transparent;color:inherit;font-family:"Cambria Math","Times New Roman",Tinos,serif;font-size:16px;line-height:1.2;cursor:pointer}
2238
2325
  .bb-eqp-sym:hover{background:var(--bb-ui-hover,#f1efe8)}
@@ -2247,32 +2334,32 @@ var STYLE10 = `
2247
2334
  .bb-eqp-st-name{font-size:10px;opacity:.6;text-align:center;line-height:1.25}
2248
2335
  .bb-eqp-st svg{display:block}
2249
2336
  `;
2250
- var CHEVRON_UP = '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M6 15l6-6 6 6"/></svg>';
2251
- var CHEVRON_DOWN = '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M6 9l6 6 6-6"/></svg>';
2252
2337
  function equationPanel(options) {
2338
+ injectStyle("bb-ui-tabs", TABS_CSS);
2253
2339
  injectStyle("bb-ui-eqp-styles", STYLE10);
2254
2340
  const root = document.createElement("div");
2255
2341
  root.className = "bb-eqp";
2256
2342
  root.setAttribute("role", "group");
2257
2343
  root.setAttribute("aria-label", "Equation palette");
2258
2344
  root.addEventListener("mousedown", (e) => e.preventDefault());
2345
+ const strip = document.createElement("div");
2346
+ strip.className = "bb-eqp-tabs";
2259
2347
  const tabs = document.createElement("div");
2260
- tabs.className = "bb-eqp-tabs";
2348
+ tabs.className = "bb-tabs";
2261
2349
  tabs.setAttribute("role", "tablist");
2350
+ strip.append(tabs);
2262
2351
  const body = document.createElement("div");
2263
2352
  body.className = "bb-eqp-body";
2264
- let collapsed = false;
2265
2353
  let current3 = "symbols";
2266
2354
  const tabEls = {};
2267
2355
  const mkTab = (id, label) => {
2268
2356
  const b = document.createElement("button");
2269
2357
  b.type = "button";
2270
- b.className = "bb-eqp-tab";
2358
+ b.className = "bb-tab";
2271
2359
  b.textContent = label;
2272
2360
  b.setAttribute("role", "tab");
2273
2361
  b.addEventListener("click", () => {
2274
2362
  current3 = id;
2275
- if (collapsed) setCollapsed(false);
2276
2363
  render();
2277
2364
  });
2278
2365
  tabEls[id] = b;
@@ -2280,22 +2367,7 @@ function equationPanel(options) {
2280
2367
  };
2281
2368
  mkTab("symbols", "Symbols");
2282
2369
  mkTab("structures", "Structures");
2283
- const fold = document.createElement("button");
2284
- fold.type = "button";
2285
- fold.className = "bb-eqp-fold";
2286
- fold.addEventListener("click", () => setCollapsed(!collapsed));
2287
- tabs.append(fold);
2288
- root.append(tabs, body);
2289
- function setCollapsed(next) {
2290
- collapsed = next;
2291
- root.classList.toggle("is-collapsed", collapsed);
2292
- fold.innerHTML = collapsed ? CHEVRON_DOWN : CHEVRON_UP;
2293
- fold.setAttribute(
2294
- "aria-label",
2295
- collapsed ? "Show palette" : "Hide palette"
2296
- );
2297
- fold.setAttribute("aria-expanded", String(!collapsed));
2298
- }
2370
+ root.append(strip, body);
2299
2371
  function symbolsBody() {
2300
2372
  const set = document.createElement("select");
2301
2373
  set.className = "bb-eqp-set";
@@ -2376,14 +2448,9 @@ function equationPanel(options) {
2376
2448
  if (current3 === "symbols") symbolsBody();
2377
2449
  else structuresBody();
2378
2450
  }
2379
- setCollapsed(false);
2380
2451
  render();
2381
2452
  return {
2382
2453
  el: root,
2383
- setCollapsed,
2384
- get collapsed() {
2385
- return collapsed;
2386
- },
2387
2454
  destroy() {
2388
2455
  root.remove();
2389
2456
  }
@@ -2435,6 +2502,176 @@ function tableGridPicker(options) {
2435
2502
  return root;
2436
2503
  }
2437
2504
 
2505
+ // packages/ui/src/lib/table-style-panel.ts
2506
+ var import_bapbong_contracts4 = require("@shadow-garden/bapbong-contracts");
2507
+ var GATES = [
2508
+ { key: "firstRow", label: "Header row" },
2509
+ { key: "lastRow", label: "Total row" },
2510
+ { key: "hBand", label: "Banded rows" },
2511
+ { key: "firstCol", label: "First column" },
2512
+ { key: "lastCol", label: "Last column" },
2513
+ { key: "vBand", label: "Banded columns" }
2514
+ ];
2515
+ var THUMB_ROWS = 4;
2516
+ var THUMB_COLS = 5;
2517
+ var STYLE12 = `
2518
+ .bb-tsp{display:flex;gap:16px;width:432px;max-width:100%;color:var(--bb-ui-fg,#2c2c2a)}
2519
+ .bb-tsp *{box-sizing:border-box}
2520
+ .bb-tsp-opts{flex:0 0 128px;display:flex;flex-direction:column;gap:2px}
2521
+ .bb-tsp-lbl{font-size:12px;opacity:.7;margin-bottom:4px}
2522
+ .bb-tsp-chk{display:flex;align-items:center;gap:7px;font-size:13px;padding:3px 0;cursor:pointer}
2523
+ .bb-tsp-chk input{margin:0}
2524
+ .bb-tsp-chk.is-disabled{opacity:.4;cursor:default}
2525
+ .bb-tsp-sep{height:1px;margin:6px 0;background:var(--bb-ui-border,#e3e3e0)}
2526
+ .bb-tsp-main{flex:1;display:flex;flex-direction:column;gap:8px;min-width:0}
2527
+ .bb-tsp-gallery{display:flex;flex-wrap:wrap;gap:6px}
2528
+ .bb-tsp-tile{padding:3px;border:1px solid var(--bb-ui-control-border,var(--bb-ui-border,#d8d6cf));border-radius:6px;background:var(--bb-ui-control-bg,var(--bb-ui-bg,#fff));cursor:pointer;line-height:0}
2529
+ .bb-tsp-tile:hover{border-color:var(--bb-ui-fg,#2c2c2a)}
2530
+ .bb-tsp-tile.is-selected{padding:2px;border:2px solid var(--bb-ui-active-fg,#0c447c)}
2531
+ .bb-tsp-thumb{display:grid;grid-template-columns:repeat(${THUMB_COLS},12px);grid-auto-rows:8px;gap:0}
2532
+ .bb-tsp-none{display:flex;align-items:center;justify-content:center;width:60px;height:32px;font-size:11px;opacity:.7;line-height:1.1}
2533
+ .bb-tsp-empty{font-size:13px;opacity:.65;padding:8px 0}
2534
+ `;
2535
+ function edgeCss(side) {
2536
+ if (!side) return "none";
2537
+ return `1px ${side.style === "double" ? "double" : "solid"} ${side.color}`;
2538
+ }
2539
+ function thumbnail(style, look) {
2540
+ const grid = document.createElement("div");
2541
+ grid.className = "bb-tsp-thumb";
2542
+ const t = style.table.borders ?? {};
2543
+ for (let r = 0; r < THUMB_ROWS; r++) {
2544
+ for (let c2 = 0; c2 < THUMB_COLS; c2++) {
2545
+ const layer = (0, import_bapbong_contracts4.cellStyleLayer)(style, look, {
2546
+ row: r,
2547
+ rowCount: THUMB_ROWS,
2548
+ col: c2,
2549
+ colspan: 1,
2550
+ colCount: THUMB_COLS,
2551
+ header: false
2552
+ });
2553
+ const cell = document.createElement("div");
2554
+ const b = layer.borders ?? {};
2555
+ cell.style.borderTop = edgeCss(b.top ?? (r === 0 ? t.top : t.insideH));
2556
+ cell.style.borderBottom = edgeCss(
2557
+ b.bottom ?? (r === THUMB_ROWS - 1 ? t.bottom : void 0)
2558
+ );
2559
+ cell.style.borderLeft = edgeCss(b.left ?? (c2 === 0 ? t.left : t.insideV));
2560
+ cell.style.borderRight = edgeCss(
2561
+ b.right ?? (c2 === THUMB_COLS - 1 ? t.right : void 0)
2562
+ );
2563
+ if (layer.background) cell.style.background = layer.background;
2564
+ grid.appendChild(cell);
2565
+ }
2566
+ }
2567
+ return grid;
2568
+ }
2569
+ function createTableStylePanel(options) {
2570
+ injectStyle("bb-table-style-panel", STYLE12);
2571
+ const root = document.createElement("div");
2572
+ root.className = "bb-tsp";
2573
+ const opts = document.createElement("div");
2574
+ opts.className = "bb-tsp-opts";
2575
+ const optsLbl = document.createElement("div");
2576
+ optsLbl.className = "bb-tsp-lbl";
2577
+ optsLbl.textContent = "Style options";
2578
+ opts.appendChild(optsLbl);
2579
+ const checks = /* @__PURE__ */ new Map();
2580
+ GATES.forEach((g2, i) => {
2581
+ if (i === 3) {
2582
+ const sep = document.createElement("div");
2583
+ sep.className = "bb-tsp-sep";
2584
+ opts.appendChild(sep);
2585
+ }
2586
+ const label = document.createElement("label");
2587
+ label.className = "bb-tsp-chk";
2588
+ const input = document.createElement("input");
2589
+ input.type = "checkbox";
2590
+ input.addEventListener(
2591
+ "change",
2592
+ () => options.onLook({ [g2.key]: input.checked })
2593
+ );
2594
+ label.append(input, document.createTextNode(g2.label));
2595
+ opts.appendChild(label);
2596
+ checks.set(g2.key, input);
2597
+ });
2598
+ const main = document.createElement("div");
2599
+ main.className = "bb-tsp-main";
2600
+ const mainLbl = document.createElement("div");
2601
+ mainLbl.className = "bb-tsp-lbl";
2602
+ mainLbl.textContent = "Table styles";
2603
+ const gallery = document.createElement("div");
2604
+ gallery.className = "bb-tsp-gallery";
2605
+ main.append(mainLbl, gallery);
2606
+ root.append(opts, main);
2607
+ const dialog = new Dialog({
2608
+ title: "Table style",
2609
+ modal: false,
2610
+ anchor: options.anchor
2611
+ });
2612
+ dialog.setContent(root);
2613
+ let drawnKey = "";
2614
+ const draw = () => {
2615
+ const current3 = options.current();
2616
+ const items = options.styles();
2617
+ const key = JSON.stringify([current3, items.map((i) => i.id)]);
2618
+ if (key === drawnKey) return;
2619
+ drawnKey = key;
2620
+ const look = current3?.look ?? null;
2621
+ for (const [gate, input] of checks) {
2622
+ input.checked = look?.[gate] ?? false;
2623
+ input.disabled = !current3;
2624
+ input.closest("label")?.classList.toggle("is-disabled", !current3);
2625
+ }
2626
+ gallery.replaceChildren();
2627
+ if (!current3) {
2628
+ const empty = document.createElement("div");
2629
+ empty.className = "bb-tsp-empty";
2630
+ empty.textContent = "Click inside a table to style it.";
2631
+ gallery.appendChild(empty);
2632
+ return;
2633
+ }
2634
+ const noneTile = document.createElement("button");
2635
+ noneTile.type = "button";
2636
+ noneTile.className = "bb-tsp-tile" + (current3.styleId === null ? " is-selected" : "");
2637
+ noneTile.title = "None";
2638
+ const noneInner = document.createElement("span");
2639
+ noneInner.className = "bb-tsp-none";
2640
+ noneInner.textContent = "None";
2641
+ noneTile.appendChild(noneInner);
2642
+ noneTile.addEventListener("click", () => options.onPick(null));
2643
+ gallery.appendChild(noneTile);
2644
+ for (const item of items) {
2645
+ const tile = document.createElement("button");
2646
+ tile.type = "button";
2647
+ tile.className = "bb-tsp-tile" + (current3.styleId === item.id ? " is-selected" : "");
2648
+ tile.title = item.name;
2649
+ tile.appendChild(thumbnail(item.style, current3.look));
2650
+ tile.addEventListener("click", () => options.onPick(item));
2651
+ gallery.appendChild(tile);
2652
+ }
2653
+ };
2654
+ return {
2655
+ open() {
2656
+ drawnKey = "";
2657
+ draw();
2658
+ dialog.open();
2659
+ },
2660
+ close() {
2661
+ dialog.close();
2662
+ },
2663
+ refresh() {
2664
+ if (dialog.isOpen) draw();
2665
+ },
2666
+ destroy() {
2667
+ dialog.destroy();
2668
+ },
2669
+ get isOpen() {
2670
+ return dialog.isOpen;
2671
+ }
2672
+ };
2673
+ }
2674
+
2438
2675
  // packages/ui/src/lib/page-setup.ts
2439
2676
  var PX_PER_CM = 96 / 2.54;
2440
2677
  var pxToCm = (px) => px / PX_PER_CM;
@@ -2442,7 +2679,7 @@ var cmToPx = (cm) => Math.round(cm * PX_PER_CM);
2442
2679
  function fmtCm(cm) {
2443
2680
  return `${Number(cm.toFixed(3))} cm`;
2444
2681
  }
2445
- var STYLE12 = `
2682
+ var STYLE13 = `
2446
2683
  .bb-ps{display:flex;flex-direction:column;min-width:250px;max-height:min(70vh,460px);overflow-y:auto}
2447
2684
  .bb-ps,.bb-ps *{box-sizing:border-box}
2448
2685
  .bb-ps-row{display:flex;align-items:center;gap:11px;width:100%;padding:7px 10px;border:0;border-radius:6px;background:transparent;color:inherit;font:inherit;text-align:left;cursor:pointer}
@@ -2568,7 +2805,7 @@ function customRow(icon, title, description, onPick) {
2568
2805
  return presetRow(icon, title, captionLine(description), false, onPick);
2569
2806
  }
2570
2807
  function pageSizePicker(options) {
2571
- injectStyle("bb-ui-pagesetup-styles", STYLE12);
2808
+ injectStyle("bb-ui-pagesetup-styles", STYLE13);
2572
2809
  const root = document.createElement("div");
2573
2810
  root.className = "bb-ps";
2574
2811
  root.setAttribute("role", "menu");
@@ -2597,7 +2834,7 @@ function pageSizePicker(options) {
2597
2834
  return root;
2598
2835
  }
2599
2836
  function orientationPicker(options) {
2600
- injectStyle("bb-ui-pagesetup-styles", STYLE12);
2837
+ injectStyle("bb-ui-pagesetup-styles", STYLE13);
2601
2838
  const root = document.createElement("div");
2602
2839
  root.className = "bb-ps";
2603
2840
  root.setAttribute("role", "menu");
@@ -2620,7 +2857,7 @@ function orientationPicker(options) {
2620
2857
  return root;
2621
2858
  }
2622
2859
  function marginPresetPicker(options) {
2623
- injectStyle("bb-ui-pagesetup-styles", STYLE12);
2860
+ injectStyle("bb-ui-pagesetup-styles", STYLE13);
2624
2861
  const root = document.createElement("div");
2625
2862
  root.className = "bb-ps";
2626
2863
  root.setAttribute("role", "menu");
@@ -2695,7 +2932,7 @@ function readPx(input, fallbackPx) {
2695
2932
  return Number.isFinite(n) && n > 0 ? cmToPx(n) : fallbackPx;
2696
2933
  }
2697
2934
  function openPageSizeDialog(options) {
2698
- injectStyle("bb-ui-pagesetup-styles", STYLE12);
2935
+ injectStyle("bb-ui-pagesetup-styles", STYLE13);
2699
2936
  const dialog = new Dialog({ title: "Paper size", modal: true });
2700
2937
  const form = document.createElement("form");
2701
2938
  form.className = "bb-pd";
@@ -2722,7 +2959,7 @@ function openPageSizeDialog(options) {
2722
2959
  width.input.select();
2723
2960
  }
2724
2961
  function openMarginsDialog(options) {
2725
- injectStyle("bb-ui-pagesetup-styles", STYLE12);
2962
+ injectStyle("bb-ui-pagesetup-styles", STYLE13);
2726
2963
  const dialog = new Dialog({ title: "Margins", modal: true });
2727
2964
  const form = document.createElement("form");
2728
2965
  form.className = "bb-pd";
@@ -2779,7 +3016,7 @@ var PGNUM_FORMATS = [
2779
3016
  ["upperLetter", "A, B, C, D\u2026", "A"]
2780
3017
  ];
2781
3018
  function pageNumberPicker(options) {
2782
- injectStyle("bb-ui-pagesetup-styles", STYLE12);
3019
+ injectStyle("bb-ui-pagesetup-styles", STYLE13);
2783
3020
  const root = document.createElement("div");
2784
3021
  root.className = "bb-ps";
2785
3022
  root.setAttribute("role", "menu");
@@ -2880,7 +3117,7 @@ function pageNumberPicker(options) {
2880
3117
  return root;
2881
3118
  }
2882
3119
  function sectionPaperPanel(options) {
2883
- injectStyle("bb-ui-pagesetup-styles", STYLE12);
3120
+ injectStyle("bb-ui-pagesetup-styles", STYLE13);
2884
3121
  const root = document.createElement("div");
2885
3122
  root.className = "bb-ps";
2886
3123
  root.setAttribute("role", "menu");
@@ -2949,7 +3186,7 @@ function sectionPaperPanel(options) {
2949
3186
  }
2950
3187
 
2951
3188
  // packages/ui/src/lib/section-chip.ts
2952
- var STYLE13 = `
3189
+ var STYLE14 = `
2953
3190
  .bb-secchip{position:absolute;z-index:8;display:inline-flex;align-items:center;transform:translate(-50%,-50%);white-space:nowrap;background:var(--bb-ui-menu-bg,#fff);-webkit-backdrop-filter:var(--bb-ui-pop-filter,none);backdrop-filter:var(--bb-ui-pop-filter,none);border:1px solid var(--bb-ui-pop-border,var(--bb-ui-border,#e3e3e0));border-radius:10px;padding:1px 2px;box-shadow:0 2px 10px rgba(0,0,0,.12);font-family:var(--bb-ui-font,system-ui,-apple-system,sans-serif);font-size:11px;color:var(--bb-ui-fg,#2c2c2a)}
2954
3191
  .bb-secchip *{box-sizing:border-box}
2955
3192
  .bb-secchip-title{padding:0 7px;font-size:11px;font-weight:600;opacity:.8}
@@ -2962,7 +3199,7 @@ var STYLE13 = `
2962
3199
  .bb-secchip-x:hover{background:var(--bb-ui-hover,#f1efe8);opacity:1}
2963
3200
  `;
2964
3201
  function createSectionChip(options) {
2965
- injectStyle("bb-ui-section-chip-styles", STYLE13);
3202
+ injectStyle("bb-ui-section-chip-styles", STYLE14);
2966
3203
  const el5 = document.createElement("div");
2967
3204
  el5.className = "bb-secchip";
2968
3205
  const title = document.createElement("span");
@@ -3019,17 +3256,13 @@ var halfToPt = (hp) => hp / 2;
3019
3256
  var ptToHalf = (pt) => Math.round(pt * 2);
3020
3257
  var SCALE_PRESETS = [200, 150, 100, 90, 80, 66, 50, 33];
3021
3258
  var SUPERSUB_SCALE = 0.66;
3022
- var STYLE14 = `
3259
+ var STYLE15 = `
3023
3260
  .bb-fd{display:flex;flex-direction:column;gap:15px;min-width:396px;max-width:430px;color:var(--bb-ui-fg,#2c2c2a)}
3024
3261
  .bb-fd *{box-sizing:border-box}
3025
3262
  /* A segmented control, not a "tab joined to its pane": that pattern needs the
3026
3263
  active tab's background to equal the pane's, and this dialog floats on
3027
3264
  frosted glass whose colour the widget cannot know. Same shape cell-properties
3028
3265
  already uses, so the two dialogs read as one product. */
3029
- .bb-fd-tabs{display:inline-flex;border:0.5px solid var(--bb-ui-control-border,var(--bb-ui-border,#d8d6cf));border-radius:6px;overflow:hidden;align-self:flex-start}
3030
- .bb-fd-tab{height:30px;padding:0 18px;border:0;border-right:0.5px solid var(--bb-ui-border,#e3e3e0);background:transparent;color:inherit;opacity:.65;font:inherit;font-size:13px;cursor:pointer}
3031
- .bb-fd-tab:last-child{border-right:0}
3032
- .bb-fd-tab[aria-selected="true"]{background:var(--bb-ui-active-bg,#e6f1fb);color:var(--bb-ui-active-fg,#0c447c);opacity:1}
3033
3266
  .bb-fd-pane{display:flex;flex-direction:column;gap:13px}
3034
3267
  /* A class-level display beats the UA's [hidden]{display:none}; without this
3035
3268
  both tabs render at once. */
@@ -3101,13 +3334,14 @@ function openFontDialog({
3101
3334
  sizes,
3102
3335
  onApply
3103
3336
  }) {
3104
- injectStyle("bb-font-dialog", STYLE14);
3337
+ injectStyle("bb-ui-tabs", TABS_CSS);
3338
+ injectStyle("bb-font-dialog", STYLE15);
3105
3339
  const dialog = new Dialog({ title: "Font", modal: true });
3106
3340
  const root = el("div", "bb-fd");
3107
- const tabs = el("div", "bb-fd-tabs");
3341
+ const tabs = el("div", "bb-tabs");
3108
3342
  tabs.setAttribute("role", "tablist");
3109
- const fontTab = el("button", "bb-fd-tab", "Font");
3110
- const advTab = el("button", "bb-fd-tab", "Advanced");
3343
+ const fontTab = el("button", "bb-tab", "Font");
3344
+ const advTab = el("button", "bb-tab", "Advanced");
3111
3345
  for (const t of [fontTab, advTab]) {
3112
3346
  t.type = "button";
3113
3347
  t.setAttribute("role", "tab");
@@ -3342,7 +3576,7 @@ function openFontDialog({
3342
3576
  }
3343
3577
 
3344
3578
  // packages/ui/src/lib/symbol-sets.ts
3345
- var import_bapbong_contracts4 = require("@shadow-garden/bapbong-contracts");
3579
+ var import_bapbong_contracts5 = require("@shadow-garden/bapbong-contracts");
3346
3580
  var g = (id, label, short, pairs) => ({
3347
3581
  id,
3348
3582
  label,
@@ -3678,7 +3912,7 @@ var SYMBOL_GROUPS = [
3678
3912
  ])
3679
3913
  ];
3680
3914
  function autoCorrectHint(char) {
3681
- return import_bapbong_contracts4.AUTOCORRECT_RULES.find((r) => r.to === char)?.seq ?? "";
3915
+ return import_bapbong_contracts5.AUTOCORRECT_RULES.find((r) => r.to === char)?.seq ?? "";
3682
3916
  }
3683
3917
  var SPECIAL_CHARACTERS = [
3684
3918
  { char: "\u2014", name: "Em dash", hint: "" },
@@ -3743,7 +3977,7 @@ function searchSymbols(query) {
3743
3977
  }
3744
3978
 
3745
3979
  // packages/ui/src/lib/symbol-dialog.ts
3746
- var STYLE15 = `
3980
+ var STYLE16 = `
3747
3981
  .bb-sd{display:flex;flex-direction:column;gap:12px;width:412px;max-width:100%;color:var(--bb-ui-fg,#2c2c2a)}
3748
3982
  .bb-sd *{box-sizing:border-box}
3749
3983
  .bb-sd-tabs{display:inline-flex;border:0.5px solid var(--bb-ui-control-border,var(--bb-ui-border,#d8d6cf));border-radius:6px;overflow:hidden;align-self:flex-start}
@@ -3804,7 +4038,7 @@ function glyphFor(char) {
3804
4038
  return char;
3805
4039
  }
3806
4040
  function createSymbolDialog(options) {
3807
- injectStyle("bb-symbol-dialog", STYLE15);
4041
+ injectStyle("bb-symbol-dialog", STYLE16);
3808
4042
  let recent = [...options.recent ?? []].slice(0, RECENT_MAX);
3809
4043
  let selected = null;
3810
4044
  const root = el2("div", "bb-sd");
@@ -4082,7 +4316,7 @@ function createSymbolDialog(options) {
4082
4316
  }
4083
4317
 
4084
4318
  // packages/ui/src/lib/symbol-popover.ts
4085
- var STYLE16 = `
4319
+ var STYLE17 = `
4086
4320
  .bb-spk{position:fixed;margin:0;max-width:none;max-height:none;z-index:1300;width:276px;padding:10px;background:var(--bb-ui-menu-bg,#fff);-webkit-backdrop-filter:var(--bb-ui-pop-filter,none);backdrop-filter:var(--bb-ui-pop-filter,none);color:var(--bb-ui-fg,#2c2c2a);border:1px solid var(--bb-ui-pop-border,var(--bb-ui-border,#e3e3e0));border-radius:10px;box-shadow:0 8px 28px rgba(0,0,0,.16);font-family:var(--bb-ui-font,system-ui,-apple-system,sans-serif);font-size:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:8px}
4087
4321
  .bb-spk *{box-sizing:border-box}
4088
4322
  .bb-spk::backdrop{background:transparent}
@@ -4115,7 +4349,7 @@ function el3(tag, cls, text) {
4115
4349
  return n;
4116
4350
  }
4117
4351
  function openSymbolPopover(options) {
4118
- injectStyle("bb-ui-symbol-popover", STYLE16);
4352
+ injectStyle("bb-ui-symbol-popover", STYLE17);
4119
4353
  const { anchor } = options;
4120
4354
  let recent = [...options.recent ?? []].slice(0, RECENT_MAX);
4121
4355
  let groupId = SYMBOL_GROUPS[0].id;
@@ -4276,8 +4510,8 @@ function openSymbolPopover(options) {
4276
4510
  }
4277
4511
 
4278
4512
  // packages/ui/src/lib/shortcuts-dialog.ts
4279
- var import_bapbong_contracts5 = require("@shadow-garden/bapbong-contracts");
4280
- var STYLE17 = `
4513
+ var import_bapbong_contracts6 = require("@shadow-garden/bapbong-contracts");
4514
+ var STYLE18 = `
4281
4515
  .bb-ks{display:flex;flex-direction:column;gap:10px;width:600px;max-width:100%;color:var(--bb-ui-fg,#2c2c2a);font-size:12px}
4282
4516
  .bb-ks *{box-sizing:border-box}
4283
4517
  .bb-ks-top{display:flex;gap:8px;align-items:center}
@@ -4335,7 +4569,7 @@ function highlight(text, q) {
4335
4569
  return out;
4336
4570
  }
4337
4571
  function openKeyboardShortcutsDialog(options) {
4338
- injectStyle("bb-ui-shortcuts-dialog", STYLE17);
4572
+ injectStyle("bb-ui-shortcuts-dialog", STYLE18);
4339
4573
  const dialog = new Dialog({
4340
4574
  title: options.title ?? "Keyboard shortcuts",
4341
4575
  modal: true,
@@ -4394,7 +4628,7 @@ function openKeyboardShortcutsDialog(options) {
4394
4628
  const rows = [];
4395
4629
  for (const src of options.sources)
4396
4630
  rows.push(
4397
- ...(0, import_bapbong_contracts5.keybindingRows)(src.keybindings, (c2) => src.commands.get(c2)?.title)
4631
+ ...(0, import_bapbong_contracts6.keybindingRows)(src.keybindings, (c2) => src.commands.get(c2)?.title)
4398
4632
  );
4399
4633
  return rows;
4400
4634
  };
@@ -4402,13 +4636,13 @@ function openKeyboardShortcutsDialog(options) {
4402
4636
  const td = el4("td");
4403
4637
  keys.forEach((k, i) => {
4404
4638
  if (i > 0) td.append(el4("span", "or", "or"));
4405
- for (const part of (0, import_bapbong_contracts5.formatKey)(k, IS_MAC))
4639
+ for (const part of (0, import_bapbong_contracts6.formatKey)(k, IS_MAC))
4406
4640
  td.append(el4("kbd", void 0, part));
4407
4641
  });
4408
4642
  return td;
4409
4643
  };
4410
4644
  const sortRows = (rows) => {
4411
- const val = (r) => sortKey === "title" ? r.title : sortKey === "keys" ? r.keys.map((k) => (0, import_bapbong_contracts5.formatKey)(k, IS_MAC).join("")).join(" ") : sortKey === "when" ? r.when ?? "" : r.source;
4645
+ const val = (r) => sortKey === "title" ? r.title : sortKey === "keys" ? r.keys.map((k) => (0, import_bapbong_contracts6.formatKey)(k, IS_MAC).join("")).join(" ") : sortKey === "when" ? r.when ?? "" : r.source;
4412
4646
  const sorted = [...rows].sort(
4413
4647
  (a, b) => val(a).localeCompare(val(b), void 0, { sensitivity: "base" })
4414
4648
  );
@@ -4417,7 +4651,7 @@ function openKeyboardShortcutsDialog(options) {
4417
4651
  const render = () => {
4418
4652
  const q = search.value.trim().toLowerCase();
4419
4653
  const rows = allRows();
4420
- const shown = sortRows((0, import_bapbong_contracts5.filterKeybindingRows)(rows, q, IS_MAC));
4654
+ const shown = sortRows((0, import_bapbong_contracts6.filterKeybindingRows)(rows, q, IS_MAC));
4421
4655
  for (const [key, th] of ths) {
4422
4656
  if (key === sortKey)
4423
4657
  th.setAttribute("aria-sort", sortAsc ? "ascending" : "descending");
@@ -4461,7 +4695,7 @@ function openKeyboardShortcutsDialog(options) {
4461
4695
  }
4462
4696
 
4463
4697
  // packages/ui/src/lib/scrollbars.ts
4464
- var STYLE18 = `
4698
+ var STYLE19 = `
4465
4699
  @property --bb-scroll-thumb-now {
4466
4700
  syntax: '<color>';
4467
4701
  /* MUST inherit: the thumb pseudo-element never matches .bb-scrolling
@@ -4493,7 +4727,7 @@ var STYLE18 = `
4493
4727
  var installed = null;
4494
4728
  function installScrollbars(root = document) {
4495
4729
  if (installed) return installed;
4496
- injectStyle("bb-ui-scrollbars", STYLE18);
4730
+ injectStyle("bb-ui-scrollbars", STYLE19);
4497
4731
  const quiet = /* @__PURE__ */ new WeakMap();
4498
4732
  const onScroll = (ev) => {
4499
4733
  const el5 = ev.target instanceof HTMLElement ? ev.target : root.documentElement;
@@ -4526,11 +4760,13 @@ function installScrollbars(root = document) {
4526
4760
  createFindDialog,
4527
4761
  createSectionChip,
4528
4762
  createSymbolDialog,
4763
+ createTableStylePanel,
4529
4764
  defaultMenus,
4530
4765
  defaultToolbarGroups,
4531
4766
  equationGallery,
4532
4767
  equationPanel,
4533
4768
  equationPreviewSvg,
4769
+ foldPlan,
4534
4770
  installScrollbars,
4535
4771
  marginPresetPicker,
4536
4772
  mountMenubar,