@bicharts/chart-host 0.5.28 → 0.5.29

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.mjs CHANGED
@@ -446,6 +446,103 @@ function actionFor(v) {
446
446
  return { kind: "keep", reason: v.errorMessage ?? v.verdictReason ?? "review failed", userVisible: false };
447
447
  }
448
448
 
449
+ // src/reviewDialog.ts
450
+ var DEFAULT_TEXT = {
451
+ title: "Apply improvements?",
452
+ fallbackReason: "The AI review suggests a change to this chart.",
453
+ cost: "Applying creates a new chart version and uses a generation. Declining costs nothing.",
454
+ decline: "No thanks",
455
+ accept: "Apply"
456
+ };
457
+ function askApplyImprovements(container, reason, opts = {}) {
458
+ return new Promise((resolve) => {
459
+ try {
460
+ const minW = opts.minWidth ?? 260;
461
+ const minH = opts.minHeight ?? 170;
462
+ const w = container.clientWidth || container.offsetWidth || 0;
463
+ const h = container.clientHeight || container.offsetHeight || 0;
464
+ if (w < minW || h < minH) {
465
+ resolve(false);
466
+ return;
467
+ }
468
+ const text = { ...DEFAULT_TEXT, ...opts.text ?? {} };
469
+ const doc = container.ownerDocument;
470
+ const ov = doc.createElement("div");
471
+ ov.id = opts.id ?? "bic-review-ask-overlay";
472
+ if (opts.overlayClass) ov.className = opts.overlayClass;
473
+ else ov.style.cssText = "position:absolute;inset:0;background:rgba(0,0,0,0.55);z-index:110;";
474
+ ov.style.display = "flex";
475
+ ov.style.alignItems = "center";
476
+ ov.style.justifyContent = "center";
477
+ const card = doc.createElement("div");
478
+ if (opts.cardClass) card.className = opts.cardClass;
479
+ else card.style.cssText = "background:#ffffff;color:#1f2937;border-radius:10px;box-shadow:0 6px 24px rgba(0,0,0,0.22);";
480
+ card.style.width = "auto";
481
+ card.style.height = "auto";
482
+ card.style.maxWidth = "min(420px, 90%)";
483
+ card.style.maxHeight = "90%";
484
+ card.style.overflow = "auto";
485
+ card.style.padding = "16px 18px";
486
+ card.style.display = "flex";
487
+ card.style.flexDirection = "column";
488
+ card.style.gap = "10px";
489
+ card.style.boxSizing = "border-box";
490
+ const head = doc.createElement("div");
491
+ head.style.cssText = "font:600 13px 'Segoe UI',system-ui,sans-serif;";
492
+ head.textContent = text.title;
493
+ const why = doc.createElement("div");
494
+ why.style.cssText = "font:400 12px/1.45 'Segoe UI',system-ui,sans-serif;white-space:normal;";
495
+ why.textContent = reason || text.fallbackReason;
496
+ const cost = doc.createElement("div");
497
+ cost.style.cssText = "font:400 11px/1.4 'Segoe UI',system-ui,sans-serif;opacity:0.75;white-space:normal;";
498
+ cost.textContent = text.cost;
499
+ const row = doc.createElement("div");
500
+ row.style.cssText = "display:flex;gap:8px;justify-content:flex-end;margin-top:4px;";
501
+ const no = doc.createElement("button");
502
+ no.type = "button";
503
+ no.textContent = text.decline;
504
+ const yes = doc.createElement("button");
505
+ yes.type = "button";
506
+ yes.textContent = text.accept;
507
+ yes.style.cssText = "font-weight:600;";
508
+ let done = false;
509
+ const finish = (v) => {
510
+ if (done) return;
511
+ done = true;
512
+ try {
513
+ if (ov.parentElement) ov.parentElement.removeChild(ov);
514
+ } catch {
515
+ }
516
+ doc.removeEventListener("keydown", onKey, true);
517
+ resolve(v);
518
+ };
519
+ const onKey = (e) => {
520
+ if (e.key === "Escape") {
521
+ e.stopPropagation();
522
+ finish(false);
523
+ }
524
+ };
525
+ no.addEventListener("click", () => finish(false));
526
+ yes.addEventListener("click", () => finish(true));
527
+ ov.addEventListener("click", (e) => {
528
+ if (e.target === ov) finish(false);
529
+ });
530
+ card.addEventListener("click", (e) => e.stopPropagation());
531
+ doc.addEventListener("keydown", onKey, true);
532
+ row.append(no, yes);
533
+ card.append(head, why, cost, row);
534
+ ov.appendChild(card);
535
+ container.appendChild(ov);
536
+ try {
537
+ yes.focus();
538
+ } catch {
539
+ }
540
+ } catch {
541
+ resolve(false);
542
+ }
543
+ });
544
+ }
545
+
449
546
  // src/index.ts
450
547
  function registerCityTable(packed) {
451
548
  L3(packed);
@@ -479,6 +576,7 @@ export {
479
576
  SELECTION_ACTIVE_CLASS,
480
577
  XFILTER_REFRESH_EVENT,
481
578
  actionFor,
579
+ askApplyImprovements,
482
580
  bareBase64,
483
581
  buildRenderPayload,
484
582
  buildReviewWire,
@@ -10,3 +10,4 @@ export { createMarkResolver, type MarkResolver, type MarkResolverEnv } from "./s
10
10
  export { planTrivialChart, compileTrivialSource, type TrivialPlan, type TrivialShapeKind } from "./trivial";
11
11
  export { captureSvgSnapshot, svgToDataUrl, svgNaturalSize, rasterizeSvgToPngDataUrl, type SnapshotOptions } from "./snapshot";
12
12
  export { shouldReview, buildReviewWire, bareBase64, actionFor, type ReviewGate, type ReviewWire, type ReviewVerdict, type ReviewAction } from "./review";
13
+ export { askApplyImprovements, type ReviewDialogOptions, type ReviewDialogText } from "./reviewDialog";
@@ -0,0 +1,23 @@
1
+ export interface ReviewDialogText {
2
+ title?: string;
3
+ /** Shown when the judge supplied no reason. The judge's reason always wins when present. */
4
+ fallbackReason?: string;
5
+ cost?: string;
6
+ decline?: string;
7
+ accept?: string;
8
+ }
9
+ export interface ReviewDialogOptions {
10
+ /** Overlay element id. Stable so tests and log-readers can find the same dialog anywhere. */
11
+ id?: string;
12
+ /** Host chrome. When given they are ADDED to the inline baseline, so a host theme (e.g. a
13
+ * dark-mode card background) can win where it should while the layout stays put. */
14
+ overlayClass?: string;
15
+ cardClass?: string;
16
+ /** The too-small-to-ask floor. Below it the dialog is a clipped smear over the chart, and
17
+ * asking illegibly for permission to spend money is worse than not asking. */
18
+ minWidth?: number;
19
+ minHeight?: number;
20
+ text?: ReviewDialogText;
21
+ }
22
+ /** Resolves true ONLY on a deliberate click of Apply. Never rejects. */
23
+ export declare function askApplyImprovements(container: HTMLElement, reason: string, opts?: ReviewDialogOptions): Promise<boolean>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bicharts/chart-host",
3
- "version": "0.5.28",
3
+ "version": "0.5.29",
4
4
  "description": "Run a BIC-generated D3 chart in any web host: compiles the generated render() function, applies the shared option defaults, resolves mark clicks (through tooltip overlays), owns the selection affordance, and translates row indices between cross-filtered charts. The same contract the BIC Power BI visual implements, minus Power BI. React bindings at @bicharts/chart-host/react.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",