@malloy-publisher/server 0.9.15 → 0.9.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. package/dist/app/api-doc.yaml +0 -2
  2. package/dist/app/assets/{EnvironmentPage-FClzTET1.js → EnvironmentPage-BfcAd2yi.js} +1 -1
  3. package/dist/app/assets/{HomePage-DY8r_SPS.js → HomePage-Ci_5k81i.js} +1 -1
  4. package/dist/app/assets/{MainPage-COdVBhUS.js → MainPage-D3QeyfA0.js} +1 -1
  5. package/dist/app/assets/{ModelPage-DpS3jA5N.js → ModelPage-Bpeml30i.js} +2 -2
  6. package/dist/app/assets/{PackagePage-CS5ePUwy.js → PackagePage-C9Ptmg2y.js} +1 -1
  7. package/dist/app/assets/RenderedResult-DxmHGZNQ.es-D0Ona1d0.js +171 -0
  8. package/dist/app/assets/{RouteError-BnzLDEen.js → RouteError-C8LT19mj.js} +1 -1
  9. package/dist/app/assets/{ThemeEditorPage-CjaJvkiM.js → ThemeEditorPage-CynRJmSm.js} +1 -1
  10. package/dist/app/assets/{ToggleButtonGroup-DpS5j6nj.js → ToggleButtonGroup-ChqidtS2.js} +1 -1
  11. package/dist/app/assets/{documentRoutes-BGPsDJa0.js → documentRoutes-DZ-IkgZk.js} +1 -1
  12. package/dist/app/assets/{editPageMounted-C8_ZEAFO.js → editPageMounted-BaIkidNI.js} +2 -2
  13. package/dist/app/assets/{index-D6CnUO9j.js → index-CIAQd_g-.js} +1 -1
  14. package/dist/app/assets/{index-Bohg2UjZ.js → index-CtRRADnY.js} +1 -1
  15. package/dist/app/assets/{index-BLeRYy_z.js → index-Cu8EElmh.js} +1 -1
  16. package/dist/app/assets/{index-JQs0UPrG.js → index-Dzz5PLbw.js} +80 -80
  17. package/dist/app/assets/index.es-DvW5H23D.js +144 -0
  18. package/dist/app/index.html +1 -1
  19. package/dist/instrumentation.mjs +1 -1
  20. package/dist/server.mjs +7 -46
  21. package/package.json +1 -1
  22. package/dist/app/assets/RenderedResult-YBzVMVH2.es-CzJTpX_s.js +0 -154
  23. package/dist/app/assets/index.es-JmXuGuMS.js +0 -144
@@ -16,7 +16,7 @@ SPDX-License-Identifier: MIT
16
16
  href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"
17
17
  />
18
18
  <title>Malloy Publisher</title>
19
- <script type="module" crossorigin src="/assets/index-JQs0UPrG.js"></script>
19
+ <script type="module" crossorigin src="/assets/index-Dzz5PLbw.js"></script>
20
20
  <link rel="stylesheet" crossorigin href="/assets/index-DUqldpo0.css">
21
21
  </head>
22
22
  <body>
@@ -167049,7 +167049,7 @@ async function withSpan(name, attributes, fn, spanTracer = tracer()) {
167049
167049
  });
167050
167050
  }
167051
167051
  // package.json
167052
- var version = "0.9.15";
167052
+ var version = "0.9.16";
167053
167053
 
167054
167054
  // src/version.ts
167055
167055
  var SERVER_VERSION = version;
package/dist/server.mjs CHANGED
@@ -328879,7 +328879,7 @@ async function withSpan(name, attributes, fn, spanTracer = tracer()) {
328879
328879
  });
328880
328880
  }
328881
328881
  // package.json
328882
- var version = "0.9.15";
328882
+ var version = "0.9.16";
328883
328883
 
328884
328884
  // src/version.ts
328885
328885
  var SERVER_VERSION = version;
@@ -353468,7 +353468,6 @@ function assertFilterGivensParse(declared, givens) {
353468
353468
 
353469
353469
  // src/service/report_manifest.ts
353470
353470
  var MAX_BUILDER_COLUMNS = 24;
353471
- var COMPONENT_FILE_SUFFIXES = [".jsx", ".tsx"];
353472
353471
  function queryTiles(tiles) {
353473
353472
  return (tiles ?? []).filter((tile) => tile.kind === "query");
353474
353473
  }
@@ -353511,11 +353510,11 @@ var ARTIFACT_PROPERTIES = [
353511
353510
  "tiles",
353512
353511
  "givens",
353513
353512
  "autorun",
353514
- "layout",
353515
- "dashboard_columns"
353513
+ "layout"
353516
353514
  ];
353517
353515
  var ARTIFACT_PROPERTY_REPLACEMENTS = {
353518
- dashboard: "The grid width is a sibling of the artifact tag, not a property of it: " + "close the artifact braces first, as `## artifact { … } dashboard { columns=N }`."
353516
+ dashboard: "The grid width is a sibling of the artifact tag, not a property of it: " + "close the artifact braces first, as `## artifact { … } dashboard { columns=N }`.",
353517
+ dashboard_columns: "The grid width is written beside the artifact tag: " + "`## artifact { … } dashboard { columns=N }`."
353519
353518
  };
353520
353519
  var ENTRY_LAYOUT_PROPERTIES = ["row", "col", "colspan", "rowspan", "break"];
353521
353520
  var PLACED_DEFAULT_ROWSPAN = { query: 5, text: 2 };
@@ -353560,7 +353559,7 @@ function readArtifactTag(tag, declaredType) {
353560
353559
  title: tagText(artifact, "title"),
353561
353560
  kind: tagText(artifact, "kind"),
353562
353561
  tiles: readTileEntries(artifact),
353563
- dashboardColumns: tag.tag("dashboard")?.has("columns") ? positiveInteger(tagNumeric(tag.tag("dashboard"), "columns")) : artifact.array("tiles") ? positiveInteger(tagNumeric(artifact, "dashboard_columns")) : undefined,
353562
+ dashboardColumns: tag.tag("dashboard")?.has("columns") ? positiveInteger(tagNumeric(tag.tag("dashboard"), "columns")) : undefined,
353564
353563
  givens,
353565
353564
  autorun
353566
353565
  };
@@ -365942,7 +365941,7 @@ For anything not covered here, call \`search_malloy_docs\` with the topic (for e
365942
365941
  {
365943
365942
  name: "malloy-reports/grid",
365944
365943
  description: "Grid layout. Reference detail for the malloy-reports skill.",
365945
- body: "# Grid layout\n\nA report with `layout=grid` places its tiles across `dashboard { columns=N }` by each tile's `# colspan` and `# break`. Everything not about placement (tiles, text tiles, givens, drill, lint) is in the skill itself.\n\n## The four tags that make a page line up\n\nCards and tiles share one grid. Publisher reads these tags off the view each tile in `tiles=[…]` names. Copy this recipe and use the same count on every grid report in the package so they read as one product:\n\n1. **`columns=12`** on the `dashboard { … }` tag after the artifact tag. Twelve divides by 2, 3, 4 and 6, so a row is even with three cards or four. The builder has no grid-width setting; set it in the file.\n2. **A `# colspan` on every tile, summing to 12 per row.** Four cards at 3, three at 4, two tiles at 6, a full-width table at 12. Omit them and every item falls to a single column, a twelfth of the width, too narrow for a line chart to draw in at all. A colspan wider than `columns` is clamped, and the package warnings say so.\n3. **`# break` on the first tile after the cards.** Otherwise it flows into the columns left beside the cards and the next tile wraps. Not needed per row: once a row sums to 12 the next item wraps on its own.\n4. **`# label=\"...\"` on every tile view and aggregate**, including the aggregates inside a table view, whose column headers are field names too. A view referenced **by name** is the exception: you can label the tile, but its own field names still reach the axes and column headers, so label the fields in the view if you want those too.\n\nAll four go on the view a tile names, which is why a thin re-declaration (`view: revenue_trend is sales_by_month`) is the place to put them: the modelled view stays reusable and each report decides its own widths. `# subtitle=\"...\"` and `# borderless` go there too; a tile reads all five. Any tile's entry in `tiles=[…]` can take `colspan`, `break` and `rowspan` directly, and wins over the view's tags: `\"overview -> kpis\" { colspan=12 rowspan=2 }`, `intro { kind=text colspan=6 break }`. That is how one view sits at two widths on two reports, how a tile on an imported view is laid out, and the only way to set a height: `rowspan=N` is N rows of 80px plus the 16px gaps between them, so `rowspan=5` is 464px. Without one a tile is as tall as its result (up to 400px); a chart fills a set height, a table scrolls in it, and a map keeps its own size. `break=false` turns off a view's `# break`. `row=N col=N` on entries place tiles at grid cells: once any tile has a `row` the report is placed, its rows are fixed 80px tracks, tiles sit exactly where they say (so one can sit under a short neighbour beside a tall one), a missing `rowspan` is 5 rows (text 2), and phones stack tiles in reading order. The Console builder writes layout on the entry, and its moves place tiles.\n\n**Do not forget `columns=` itself.** Without it the grid falls back to a narrow default and every colspan is clamped to it, which is the usual reason a page comes out one item per row; the package warnings name it. `dashboard_columns=N` inside the artifact tag is a deprecated alias: read when `columns` is absent, an error when the two disagree. Any other property inside the artifact tag that Publisher does not read is a package warning naming it.\n\n## The traps\n\n- **A KPI row is a view of only measures.** A tile is one whole result, so a tile that comes back as one row of measures renders as big-value cards on its own (`# big_value` on the view says so explicitly; `# table` opts out).\n- **No `# size=fill` on a grid tile.** It measures against the container the whole grid was handed, not the tile, so it yields a chart thousands of pixels tall. Tiles already size to their colspan.\n- **A table tile sitting in the left part of a wide card with the rest blank** is not filled by the grid. `# table { size=fill }` on the view forces it. That is the table's own tag, not the chart `# size=fill` above.\n- **A KPI card's label is one line that ellipses** rather than wrapping. Widen the card or shorten the label.\n- **A ratio needs a number format.** `order_count / customer_count` renders as `10.695` on a card; `# number=\"#,##0.0\"` is the precision it carries.\n- **A `# shape_map` legend is titled with the measure's field _name_, not its `# label`.** Rename it in the view: `aggregate: revenue is total_sales`. Renaming drops the measure's format tags, so re-tag `# currency` at the rename.\n- **A series legend sizes itself from the longer of the series label and its widest value**, then truncates both. A 4-character label over 4-digit years clips to `20…`; `# label=\"Order year\"` instead of `\"Year\"` buys the room.\n\nThe last two are upstream renderer behavior, cheap to work around in the model. Height is the one thing the surface decides: a grid caps each tile.\n\n## Lint findings specific to a grid\n\n- A non-positive `dashboard { columns }`, or one above 24.\n- A tile view's `# colspan` that is not a positive integer, or is wider than the grid (clamped).\n- A `dashboard_columns=` alias, an error when it disagrees with `columns`.\n- A tile entry whose `colspan` or `rowspan` is not a positive integer, or that carries a property other than `row`, `col`, `colspan`, `rowspan` and `break`; an entry `colspan` wider than the grid; a placed tile past the right edge, or two placed tiles covering each other.\n\n## One query as the whole page\n\nA `# dashboard` query lays out its one result itself: top-level `aggregate:` measures are KPI cards, each `nest:` a tile. List it as the report's one tile, a model-level named query:\n\n```malloy\n## artifact { layout=grid title=\"Overview\" givens { REGION=f'US' } tiles=[\"overview\"] }\n# dashboard { columns=12 }\nquery: overview is orders -> { … }\n```\n\nA `# artifact` on the `query:` itself (the older single-query form) is not read: the file is an ordinary model. Move the tag to model level as above.\n\n- It cannot span sources, and a guessed field inside it fails the whole package load, where a bad tile fails alone.\n- A top-level `aggregate:` there is the KPI card; do not nest a `# big_value` view for it (that renders each measure as a full-width bar in one tile). Nest views for tiles; give each a `# colspan` and the first tile after the cards a `# break`. `# dashboard { gap=N table { max_height=N | none } }` are options only a `# dashboard` query reads.\n- **One plain nested table, every `# colspan` and `# break` dropped**: a `f'…'` filter literal in a `givens { … }` block shares a line with `# dashboard`. Put `# dashboard` on its own line.\n- **Nothing lines up**: `# colspan` without `columns=N`, so items flow at their natural widths.\n\nTo tell those two apart, run the tile, `{\"query\": \"run: overview\"}`, and read `renderLogs` on the response: `Unknown render tag 'colspan'` means the renderer never saw a `# dashboard` tag (check both causes); `Ignored # colspan … only applies in columns mode` means it saw the tag but no count. Neither reaches the package warnings."
365944
+ body: "# Grid layout\n\nA report with `layout=grid` places its tiles across `dashboard { columns=N }` by each tile's `# colspan` and `# break`. Everything not about placement (tiles, text tiles, givens, drill, lint) is in the skill itself.\n\n## The four tags that make a page line up\n\nCards and tiles share one grid. Publisher reads these tags off the view each tile in `tiles=[…]` names. Copy this recipe and use the same count on every grid report in the package so they read as one product:\n\n1. **`columns=12`** on the `dashboard { … }` tag after the artifact tag. Twelve divides by 2, 3, 4 and 6, so a row is even with three cards or four. The builder has no grid-width setting; set it in the file.\n2. **A `# colspan` on every tile, summing to 12 per row.** Four cards at 3, three at 4, two tiles at 6, a full-width table at 12. Omit them and every item falls to a single column, a twelfth of the width, too narrow for a line chart to draw in at all. A colspan wider than `columns` is clamped, and the package warnings say so.\n3. **`# break` on the first tile after the cards.** Otherwise it flows into the columns left beside the cards and the next tile wraps. Not needed per row: once a row sums to 12 the next item wraps on its own.\n4. **`# label=\"...\"` on every tile view and aggregate**, including the aggregates inside a table view, whose column headers are field names too. A view referenced **by name** is the exception: you can label the tile, but its own field names still reach the axes and column headers, so label the fields in the view if you want those too.\n\nAll four go on the view a tile names, which is why a thin re-declaration (`view: revenue_trend is sales_by_month`) is the place to put them: the modelled view stays reusable and each report decides its own widths. `# subtitle=\"...\"` and `# borderless` go there too; a tile reads all five. Any tile's entry in `tiles=[…]` can take `colspan`, `break` and `rowspan` directly, and wins over the view's tags: `\"overview -> kpis\" { colspan=12 rowspan=2 }`, `intro { kind=text colspan=6 break }`. That is how one view sits at two widths on two reports, how a tile on an imported view is laid out, and the only way to set a height: `rowspan=N` is N rows of 80px plus the 16px gaps between them, so `rowspan=5` is 464px. Without one a tile is as tall as its result (up to 400px); a chart fills a set height, a table scrolls in it, and a map keeps its own size. `break=false` turns off a view's `# break`. `row=N col=N` on entries place tiles at grid cells: once any tile has a `row` the report is placed, its rows are fixed 80px tracks, tiles sit exactly where they say (so one can sit under a short neighbour beside a tall one), a missing `rowspan` is 5 rows (text 2), and phones stack tiles in reading order. The Console builder writes layout on the entry, and its moves place tiles.\n\n**Do not forget `columns=` itself.** Without it the grid falls back to a narrow default and every colspan is clamped to it, which is the usual reason a page comes out one item per row; the package warnings name it. `dashboard_columns=N` inside the artifact tag is not read (it is reported, with the fix). Any other property inside the artifact tag that Publisher does not read is a package warning naming it.\n\n## The traps\n\n- **A KPI row is a view of only measures.** A tile is one whole result, so a tile that comes back as one row of measures renders as big-value cards on its own (`# big_value` on the view says so explicitly; `# table` opts out).\n- **No `# size=fill` on a grid tile.** It measures against the container the whole grid was handed, not the tile, so it yields a chart thousands of pixels tall. Tiles already size to their colspan.\n- **A table tile sitting in the left part of a wide card with the rest blank** is not filled by the grid. `# table { size=fill }` on the view forces it. That is the table's own tag, not the chart `# size=fill` above.\n- **A KPI card's label is one line that ellipses** rather than wrapping. Widen the card or shorten the label.\n- **A ratio needs a number format.** `order_count / customer_count` renders as `10.695` on a card; `# number=\"#,##0.0\"` is the precision it carries.\n- **A `# shape_map` legend is titled with the measure's field _name_, not its `# label`.** Rename it in the view: `aggregate: revenue is total_sales`. Renaming drops the measure's format tags, so re-tag `# currency` at the rename.\n- **A series legend sizes itself from the longer of the series label and its widest value**, then truncates both. A 4-character label over 4-digit years clips to `20…`; `# label=\"Order year\"` instead of `\"Year\"` buys the room.\n\nThe last two are upstream renderer behavior, cheap to work around in the model. Height is the one thing the surface decides: a grid caps each tile.\n\n## Lint findings specific to a grid\n\n- A non-positive `dashboard { columns }`, or one above 24.\n- A tile view's `# colspan` that is not a positive integer, or is wider than the grid (clamped).\n- A `dashboard_columns=` inside the artifact tag, which is not read.\n- A tile entry whose `colspan` or `rowspan` is not a positive integer, or that carries a property other than `row`, `col`, `colspan`, `rowspan` and `break`; an entry `colspan` wider than the grid; a placed tile past the right edge, or two placed tiles covering each other.\n\n## One query as the whole page\n\nA `# dashboard` query lays out its one result itself: top-level `aggregate:` measures are KPI cards, each `nest:` a tile. List it as the report's one tile, a model-level named query:\n\n```malloy\n## artifact { layout=grid title=\"Overview\" givens { REGION=f'US' } tiles=[\"overview\"] }\n# dashboard { columns=12 }\nquery: overview is orders -> { … }\n```\n\nA `# artifact` on the `query:` itself (the older single-query form) is not read: the file is an ordinary model. Move the tag to model level as above.\n\n- It cannot span sources, and a guessed field inside it fails the whole package load, where a bad tile fails alone.\n- A top-level `aggregate:` there is the KPI card; do not nest a `# big_value` view for it (that renders each measure as a full-width bar in one tile). Nest views for tiles; give each a `# colspan` and the first tile after the cards a `# break`. `# dashboard { gap=N table { max_height=N | none } }` are options only a `# dashboard` query reads.\n- **One plain nested table, every `# colspan` and `# break` dropped**: a `f'…'` filter literal in a `givens { … }` block shares a line with `# dashboard`. Put `# dashboard` on its own line.\n- **Nothing lines up**: `# colspan` without `columns=N`, so items flow at their natural widths.\n\nTo tell those two apart, run the tile, `{\"query\": \"run: overview\"}`, and read `renderLogs` on the response: `Unknown render tag 'colspan'` means the renderer never saw a `# dashboard` tag (check both causes); `Ignored # colspan … only applies in columns mode` means it saw the tag but no count. Neither reaches the package warnings."
365946
365945
  },
365947
365946
  {
365948
365947
  name: "malloy-reports/linear",
@@ -384575,7 +384574,6 @@ function lintNotebookText(modelPath, text) {
384575
384574
  const tag2 = motlyTag([tagNote.text])?.tag("artifact");
384576
384575
  if (!tag2)
384577
384576
  return;
384578
- const properties = Object.keys(tag2.dict ?? {});
384579
384577
  lintLayout(tagNote.line, tagText(tag2, "layout"));
384580
384578
  if (linear) {
384581
384579
  for (const entry of tag2.array("tiles") ?? []) {
@@ -384598,16 +384596,6 @@ function lintNotebookText(modelPath, text) {
384598
384596
  add(tagNote.line, "notebook-columns-ignored", "`dashboard { columns }` other than 1 is ignored on a notebook, which is always one column. Fix: remove `dashboard { columns }`.");
384599
384597
  }
384600
384598
  }
384601
- if (properties.includes("dashboard_columns") && properties.includes("tiles")) {
384602
- const alias = tagText(tag2, "dashboard_columns") ?? "";
384603
- const dashboardTag = motlyTag(modelNotes)?.tag("dashboard");
384604
- const canonical = dashboardTag?.has("columns") ? tagText(dashboardTag, "columns") ?? "" : undefined;
384605
- if (canonical !== undefined && tagNumeric(tag2, "dashboard_columns") !== tagNumeric(dashboardTag, "columns")) {
384606
- add(tagNote.line, "notebook-columns-conflict", `\`dashboard_columns=${alias}\` in the artifact tag and \`dashboard { columns=${canonical} }\` disagree about the grid width. Fix: keep \`dashboard { columns=… }\` and remove \`dashboard_columns\`.`, "error");
384607
- } else {
384608
- add(tagNote.line, "notebook-columns-alias", `\`dashboard_columns=${alias}\` is a deprecated spelling of the grid width. Fix: write \`dashboard { columns=${alias} }\` instead.`);
384609
- }
384610
- }
384611
384599
  }
384612
384600
  }
384613
384601
  function reportedByDashboardLint(finding, modelPath, dashboardFindings) {
@@ -384744,7 +384732,6 @@ var PREAGG_WARN_ONLY = new Set([
384744
384732
  "non_public_measure",
384745
384733
  "grain_dimension_not_public"
384746
384734
  ]);
384747
- var MALLOYYO_DASHBOARDS_DIR = "dashboards";
384748
384735
  function toTableNameManifest(entries) {
384749
384736
  const out = {};
384750
384737
  for (const [sourceEntityId, entry] of Object.entries(entries)) {
@@ -386252,16 +386239,13 @@ class Package {
386252
386239
  for (const finding of lintGivenTags(drillFacts)) {
386253
386240
  warnings.push(finding);
386254
386241
  }
386255
- for (const finding of await this.unsupportedComponentWarnings()) {
386256
- warnings.push(finding);
386257
- }
386258
386242
  } catch (err) {
386259
386243
  logger.warn("Report lint failed", {
386260
386244
  packageName: this.packageName,
386261
386245
  error: errMessage(err)
386262
386246
  });
386263
386247
  warnings.push({
386264
- message: `Report lint stopped early, so this list is incomplete: an ` + `unknown subset of the tile, report, drill, given, curation ` + `and unsupported-component checks did not run. Treat a missing ` + `finding of any of those kinds as unknown rather than clean. ` + `Reload the package to run the lint again. The ` + `reports themselves are unaffected, because they are ` + `discovered before the lint runs. An operator can find the ` + `cause in the server log under "Report lint failed".`,
386248
+ message: `Report lint stopped early, so this list is incomplete: an ` + `unknown subset of the tile, report, drill, given and curation ` + `checks did not run. Treat a missing ` + `finding of any of those kinds as unknown rather than clean. ` + `Reload the package to run the lint again. The ` + `reports themselves are unaffected, because they are ` + `discovered before the lint runs. An operator can find the ` + `cause in the server log under "Report lint failed".`,
386265
386249
  severity: "warn"
386266
386250
  });
386267
386251
  }
@@ -386314,29 +386298,6 @@ class Package {
386314
386298
  }
386315
386299
  return warnings;
386316
386300
  }
386317
- async unsupportedComponentWarnings() {
386318
- const dir = safeJoinUnderRoot(this.packagePath, MALLOYYO_DASHBOARDS_DIR);
386319
- let entries;
386320
- try {
386321
- entries = await fs15.readdir(dir);
386322
- } catch {
386323
- return [];
386324
- }
386325
- const warnings = [];
386326
- for (const entry of entries.sort()) {
386327
- const suffix = COMPONENT_FILE_SUFFIXES.find((s2) => entry.endsWith(s2));
386328
- if (!suffix)
386329
- continue;
386330
- const base = entry.slice(0, -suffix.length);
386331
- warnings.push({
386332
- model: `${MALLOYYO_DASHBOARDS_DIR}/${entry}`,
386333
- subject: base,
386334
- message: `Custom dashboard components are not supported, so ` + `"${MALLOYYO_DASHBOARDS_DIR}/${entry}" is ignored. Any ` + `${MALLOYYO_DASHBOARDS_DIR}/${base}${MODEL_FILE_SUFFIX} beside it still ` + `renders from its tags. For a page that runs its own code, use ` + `an HTML data app in the package's public/ directory.`,
386335
- severity: "warn"
386336
- });
386337
- }
386338
- return warnings;
386339
- }
386340
386301
  servedReportPaths() {
386341
386302
  return this.reports.keys();
386342
386303
  }
package/package.json CHANGED
@@ -19,7 +19,7 @@
19
19
  "snowflake"
20
20
  ],
21
21
  "homepage": "https://docs.malloydata.dev/documentation/user_guides/publishing/mcp_agents",
22
- "version": "0.9.15",
22
+ "version": "0.9.16",
23
23
  "main": "dist/server.mjs",
24
24
  "bin": {
25
25
  "malloy-publisher": "dist/server.mjs"
@@ -1,154 +0,0 @@
1
- import{ae as ye,j as O,I as ne,r as f,aU as pe,U as fe,aV as ge,aH as ve,aW as we,aX as Y,aY as ke,aZ as xe,a_ as Ce,a$ as Ae,b0 as Se,b1 as Ee,b2 as ce,b3 as le}from"./index-JQs0UPrG.js";const w=ye.drillCell;function Me(e,t){const r=new Set;let a;try{a=e.getMetadata()?.getAllFields()??[]}catch{return r}const o=new Set;for(const n of a)!n||t(n)||n.tag?.tag("hidden")===void 0&&o.add(n.tag?.text("label")??n.name);for(const n of a){if(!n||!t(n))continue;const l=n.tag?.text("label")??n.name;o.has(l)||r.add(l)}return r}function B(e){const t=/^\s*(\d+)/.exec(e.style?.gridColumn??"");return t?t[1]:void 0}function ze(e,t){if(t.size!==0)for(const r of e.querySelectorAll(".malloy-table")){const a=l=>l.closest(".malloy-table")===r,o=new Set;for(const l of r.querySelectorAll(".column-cell.th")){if(!a(l))continue;const i=(l.textContent??"").replace(/\u200b/g,"").trim(),y=B(l);y&&t.has(i)&&o.add(y)}if(o.size===0)continue;const n=new Set;for(const l of r.querySelectorAll(`.${w}[tabindex="0"]`)){if(!a(l))continue;const i=B(l);i&&(n.has(i)?l.tabIndex=-1:n.add(i))}for(const l of r.querySelectorAll(".column-cell.td")){if(!a(l))continue;const i=B(l),y=!l.querySelector(".value-null")&&(l.textContent??"").trim()!=="";if(i&&y&&o.has(i)&&!l.querySelector(".malloy-table")){if(l.classList.add(w),l.tabIndex===0)n.add(i);else{const g=n.has(i);g||n.add(i),l.tabIndex=g?-1:0}l.setAttribute("role","button")}}}}function je(e){const t=e.closest(".malloy-table"),r=B(e);return!t||!r?[]:Array.from(t.querySelectorAll(`.${w}`)).filter(a=>a.closest(".malloy-table")===t&&B(a)===r)}function ie(e,t){const r=je(e),a=r.indexOf(e);if(a===-1)return;const o=t==="first"?r[0]:t==="last"?r[r.length-1]:r[Math.min(Math.max(a+t,0),r.length-1)];if(o){for(const n of r)n.tabIndex=n===o?0:-1;return o}}function Fe(e){return{background:e.dashboardRoot,tableBackground:e.tableBackground,tableHeaderColor:e.tableHeader,tableBodyColor:e.tableBody,tableBorder:e.border,tablePinnedBackground:e.tableHeaderBackground,tablePinnedBorder:e.pinnedBorder,tableFontSize:`${e.font.size}px`,fontFamily:e.font.family,mapColor:e.mapColor}}function _e(e){const{foreground:t,axisFaint:r,gridline:a,font:o}=e,n={background:e.background,font:o.family,title:{color:t,font:o.family},axis:{labelColor:t,titleColor:t,domainColor:r,tickColor:r,gridColor:a,labelFont:o.family,titleFont:o.family},legend:{labelColor:t,titleColor:t,labelFont:o.family,titleFont:o.family},header:{labelColor:t,titleColor:t},range:{category:e.series}},l={...n,legend:{...n.legend,orient:"bottom"}};return i=>i==="shape_map"?l:n}function Re(e){if(!e)return;const t=e.tag("theme");if(!t)return;const r={},a=t.tag("palette");if(a){const n={},l=a.textArray("series");l&&l.length>0&&(n.series=l);for(const i of pe){const y=a.tag(i);if(!y)continue;const g={},x=y.text("light"),k=y.text("dark");x&&(g.light=x),k&&(g.dark=k),Object.keys(g).length>0&&(n[i]=g)}Object.keys(n).length>0&&(r.palette=n)}const o=t.tag("font");if(o){const n={},l=o.text("family");l&&(n.family=l);const i=o.numeric("size");typeof i=="number"&&Number.isFinite(i)&&(n.size=i),Object.keys(n).length>0&&(r.font=n)}return Object.keys(r).length>0?r:void 0}const K=e=>(e??[]).map(t=>t?.value).filter(t=>typeof t=="string");function $e(e){const t=e.replace(/[-_]+/g," ").replace(/\s+/g," ").trim();return t.charAt(0).toUpperCase()+t.slice(1)}const Oe=e=>K(e.annotations).some(t=>{const r=t.trim();return/^#[^("]/.test(r)&&/(?:^#|[\s{])label\s*=/.test(r)}),Te=e=>`"${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`,Be=e=>e.type?.element_type?.fields??e.type?.fields;function he(e,t){let r=!1;return{fields:e.map(a=>{const o=Be(a);if(o){const l=he(o,ce(K(a.annotations)));if(!l.changed)return a;r=!0;const i=a.type?.element_type?{...a.type,element_type:{...a.type.element_type,fields:l.fields}}:{...a.type,fields:l.fields};return{...a,type:i}}if(!t||!a.name||Oe(a))return a;const n=$e(a.name);return n===a.name?a:(r=!0,{...a,annotations:[{value:`# label=${Te(n)}
2
- `},...a.annotations??[]]})}),changed:r}}function De(e){if(!e||typeof e!="object")return e;const t=e,r=t.schema?.fields;if(!Array.isArray(r))return e;const{fields:a,changed:o}=he(r,ce(K(t.annotations)));return o?{...t,schema:{...t.schema,fields:a}}:e}const se=320,Le=10,Ie=96,qe=4e3;function Ne(e,t){if(e!=="bar"&&e!=="combo"&&e!=="line"&&e!=="chart"&&e!=="scatter_chart")return;if(e!=="bar"&&e!=="combo")return se;const r=t?.data?.array_value?.length,a=r===void 0?0:r*Le+Ie;return Math.min(Math.max(se,a),qe)}const T="data-publisher-fill",Pe=async(e,t)=>{if(typeof window>"u")throw new Error("MalloyRenderer can only be used in browser environment");const{MalloyRenderer:r}=await xe();return new r({onClick:t,vegaConfigOverride:_e(e),theme:Fe(e),onError:a=>{console.error("Error rendering visualization:",typeof a,a)}}).createViz()};async function Ve(e){if(!e||typeof e!="object")return;const t=e,r=[...Array.isArray(t.model_annotations)?t.model_annotations:[],...Array.isArray(t.source_annotations)?t.source_annotations:[],...Array.isArray(t.annotations)?t.annotations:[]].map(o=>typeof o?.value=="string"?o.value:void 0).filter(o=>!!o);if(r.length===0)return;let a;try{({parseAnnotation:a}=await ke())}catch{return}try{const{tag:o}=a(r);return Re(o)}catch(o){console.warn("Failed to parse # theme.* annotations; chart will render with the shell theme.",{error:o,lines:r});return}}function He(e,t,r){const a=Ce(t,r);for(const[o,n]of Object.entries(a))e.style.setProperty(o,n)}const Ue=`
3
- /* Card geometry, through the renderer's OWN theme vars rather than over the top
4
- of them. It declares --malloy-theme--dashboard-* on .malloy-render itself and
5
- passes each through to the --malloy-render--* the card CSS reads, so a value
6
- inherited from our wrapper loses to that declaration however early it is set:
7
- this has to be a rule on the same element, at higher specificity. Doubling the
8
- class does that, and then no !important is needed — which matters, because
9
- !important here also lands on .dashboard-item-borderless and repaints a card
10
- the author asked to have no card. */
11
- .malloy-render.malloy-render {
12
- --malloy-theme--dashboard-card-radius: var(--publisher-dashboard-card-radius);
13
- --malloy-theme--dashboard-card-padding: var(--publisher-dashboard-card-padding);
14
- }
15
- /* dashboard.css hardcodes background: #f7f9fc on .malloy-dashboard
16
- and .dashboard-row-header, which would paint light grey in dark
17
- mode and also bleed an operator-picked palette.background across
18
- the panel chrome in light. Both surfaces use
19
- --malloy-render--background here, which buildMalloyExplicitTheme
20
- wires to dashboardRoot: a mode-keyed neutral that stays decoupled
21
- from palette.background (the chart canvas) on purpose. Selectors
22
- are duplicated at higher specificity to beat the renderer s own
23
- scoped rules. */
24
- .malloy-render .malloy-dashboard,
25
- .malloy-render.malloy-render .malloy-dashboard,
26
- div.malloy-render .malloy-dashboard {
27
- /* --publisher-dashboard-root is set on the outer Publisher wrapper
28
- (see buildTableCssVars) and stays decoupled from
29
- --malloy-render--background, which gets shadowed inside the
30
- renderer when a theme.background annotation is present. The
31
- dedicated var means the panel between tiles always paints the
32
- operator neutral dashboardRoot regardless of annotations. */
33
- background: var(--publisher-dashboard-root) !important;
34
- background-color: var(--publisher-dashboard-root) !important;
35
- color: var(--malloy-render--table-body-color) !important;
36
- }
37
- .malloy-render .malloy-dashboard .dashboard-row,
38
- .malloy-render .malloy-dashboard .dashboard-row-body,
39
- .malloy-render .malloy-dashboard .dashboard-row-header,
40
- .malloy-render.malloy-render .malloy-dashboard .dashboard-row-header,
41
- div.malloy-render .malloy-dashboard .dashboard-row-header {
42
- /* Belt + suspenders: dashboard.css hardcodes .dashboard-row-header
43
- to #f7f9fc, and row / row-body have no explicit background but
44
- get caught in any annotation-driven inline writes. Force all of
45
- them to the neutral panel colour. */
46
- background: var(--publisher-dashboard-root) !important;
47
- background-color: var(--publisher-dashboard-root) !important;
48
- }
49
- .malloy-render .malloy-dashboard .dashboard-item:not(.dashboard-item-borderless) {
50
- /* Tile padding around each chart / table. Uses our custom
51
- --malloy-render--tile-background so the operator can theme the
52
- tile separately from the table header row (which paints from
53
- --malloy-render--table-pinned-background below).
54
-
55
- A flat bordered card rather than the renderer's shadow ring, which is what
56
- makes it match the composite tile's Paper. :not(borderless) because
57
- # borderless asks for no card at all, and an !important background and
58
- border drew one anyway. */
59
- background: var(--malloy-render--tile-background) !important;
60
- color: var(--malloy-render--table-body-color) !important;
61
- box-shadow: none !important;
62
- /* The card edge, not the table gridline: --publisher-dashboard-card-border,
63
- the same value TileCard paints, so the renderer's card and the composite
64
- one are outlined identically. */
65
- border: var(--publisher-dashboard-card-border) !important;
66
- }
67
- /* The renderer's big-value card — the small card a "# big_value" measure sits
68
- in, several to a row inside one tile. (No backticks in here: this is inside
69
- a template literal.) Its own CSS gives it a 0.55px edge in
70
- #e5e7eb: a sub-pixel width, and a grey off the slate ramp everything else on
71
- the page is on. It is a dashboard card like any other, so it takes the card
72
- edge and stops being the one card outlined in a hairline nobody can see. */
73
- .malloy-render .malloy-big-value-card {
74
- border: var(--publisher-dashboard-card-border) !important;
75
- /* Its shadow is a four-layer Tailwind stack, and only two of those layers
76
- are a shadow: the other is "0 0 0 1px #e5e7eb", a spread ring drawn as a
77
- second border OVER the real one. So the border above was landing
78
- underneath a ring in the renderer's own grey, and the card kept both a
79
- lifted look the flat cards around it do not have and an edge we did not
80
- pick. Dropped whole rather than rebuilt layer by layer: the ring's job is
81
- the border's job, and the border is already doing it. */
82
- box-shadow: none !important;
83
- }
84
- /* The strip of big-value cards as a grid, so cards share a width and the last one does not wrap
85
- alone. The renderer's wrapping flex row sizes each card to its own text. The 250px floor is the
86
- widest realistic value: it is nowrap at 32px in an overflow:visible card, so a narrower track
87
- would spill it onto its neighbour. Embedded (a nested big_value) is left as the renderer draws it. */
88
- .malloy-render .malloy-big-value:not(.malloy-big-value--embedded) {
89
- display: grid;
90
- grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
91
- }
92
- .malloy-render .malloy-big-value-card:not(.malloy-big-value-card--embedded) {
93
- width: auto;
94
- max-width: none;
95
- }
96
- .malloy-render .malloy-dashboard .dashboard-row-header-separator {
97
- background: var(--malloy-render--table-border) !important;
98
- }
99
- /* !important because the malloy-explorer stylesheet pins these same properties with it. */
100
- [${T}] .malloy-table.root {
101
- height: 100% !important;
102
- align-content: start !important;
103
- grid-template-columns: repeat(var(--total-header-size), minmax(max-content, 1fr)) !important;
104
- }
105
- .malloy-render .malloy-table .th.column-cell {
106
- /* Non-pinned tables have no header background in the renderer's
107
- own CSS (only pinned scrolled tables paint the pinned-header
108
- with this var). Force the same value here so every table has a
109
- visible header band reflecting the operator's choice. */
110
- background: var(--malloy-render--table-pinned-background) !important;
111
- }
112
- .malloy-render .dashboard-item-title,
113
- .malloy-render .dashboard-dimension-name {
114
- color: var(--malloy-render--label-color) !important;
115
- }
116
- .malloy-render .dashboard-item-value,
117
- .malloy-render .dashboard-item-value-measure,
118
- .malloy-render .dashboard-dimension-value {
119
- color: var(--malloy-render--value-color) !important;
120
- }
121
- .malloy-render .malloy-table,
122
- .malloy-render .malloy-list {
123
- color: var(--malloy-render--table-body-color) !important;
124
- }
125
- .malloy-render .column-cell.th,
126
- .malloy-render .cell-content.header {
127
- color: var(--malloy-render--table-header-color) !important;
128
- }
129
- /* A cell whose column declares a "# drill" this surface can honor, marked
130
- by markDrillableCells. Ordinary text with a pointer cursor until hovered,
131
- then it reads as a link: the same affordance Malloyyo gives the same tag,
132
- and the only thing that tells a reader a cell navigates. Kept off the
133
- resting state deliberately: a column painted blue competes with the data.
134
- (No backticks in here: this is inside a template literal.) */
135
- .malloy-render .${w} {
136
- cursor: pointer;
137
- }
138
- .malloy-render .${w}:hover > .cell-content {
139
- color: var(--publisher-drill-link) !important;
140
- text-decoration: underline;
141
- }
142
- /* Keyboard focus reads the same as hover, plus a ring: a hover colour is not
143
- something a keyboard user can produce, and a focus ring is not something a
144
- mouse user sees. focus-visible rather than focus, so a click does not leave a
145
- ring behind it. (No backticks in here: this is inside a template literal.) */
146
- .malloy-render .${w}:focus-visible {
147
- outline: 2px solid var(--publisher-drill-link);
148
- outline-offset: -2px;
149
- }
150
- .malloy-render .${w}:focus-visible > .cell-content {
151
- color: var(--publisher-drill-link) !important;
152
- text-decoration: underline;
153
- }
154
- `,de="publisher-malloy-renderer-overrides";function We(){if(typeof document>"u"||document.getElementById(de))return;const e=document.createElement("style");e.id=de,e.textContent=Ue,document.head.appendChild(e)}function Xe({result:e,height:t,drill:r,onSizeChange:a,onSizing:o,fill:n}){const l=f.useRef(null),i=f.useRef(r);i.current=r;const y=f.useCallback(d=>{i.current?.onClick(d)},[]),g=r!==void 0,x=f.useRef(!1),k=f.useRef(null),P=f.useRef(0),{theme:D,layers:V,mode:J}=fe(),Z=f.useMemo(()=>({radius:D.ui.radius.tile,padding:`${ge}px`}),[D.ui.radius.tile]);return f.useEffect(()=>()=>{P.current+=1;const d=k.current;d?.viz.remove(),d?.node.remove(),k.current=null},[]),f.useLayoutEffect(()=>{if(!l.current||!e)return;We();const d=l.current,ue=P.current+=1;let H=!1;const U=()=>!H&&ue===P.current;let c,b,L=null,W=0,I=null,q=null,N=null,j=null,F=null,_=null,G=null,Q=0,A=null;x.current=!1;let p,S;const X=v=>{if(H||!v.firstElementChild||Y(p,S)==="container")return;const R=Ae(p,S);if(x.current&&!R)return;const $=d.hasAttribute(T);$&&d.removeAttribute(T);const E=Se(v,le(p));$&&d.setAttribute(T,""),E>0&&(x.current=!0,a&&E!==Q&&(Q=E,a(E)));const h=Ee(v,le(p));R&&h&&G!==h&&(G=h,_?.disconnect(),_=new ResizeObserver(()=>X(v)),_.observe(h))};return(async()=>{let v;try{v=JSON.parse(e)}catch(m){console.error("Error parsing visualization result:",m);return}const R=await Ve(v),$=R?ve([...V,we(V,R)],J):D;if(!U())return;try{b=await Pe($,y)}catch(m){console.error("Failed to create renderer:",m);return}if(!U()){b.remove(),b=void 0;return}const E=k.current;c=document.createElement("div"),c.style.width="100%",c.style.height="100%",He(c,$,Z),d.childElementCount>0&&(d.style.position="relative",c.style.position="absolute",c.style.inset="0",c.style.visibility="hidden"),d.appendChild(c);const h=c;j=new MutationObserver(()=>{F&&clearTimeout(F),F=setTimeout(()=>{X(h),x.current&&j?.disconnect()},100)}),j.observe(c,{childList:!0,subtree:!0,attributes:!0});const me=b;let ee=!1;const te=()=>{if(!(ee||!U())){ee=!0,A&&(clearTimeout(A),A=null),E?.viz.remove();for(const m of Array.from(d.children))m!==h&&d.removeChild(m);h.style.position="",h.style.inset="",h.style.visibility="",d.style.position="",k.current={viz:me,node:h},X(h)}};try{b.setResult(De(v));try{const C=b.getMetadata(),M=C?.getRootField();p=M?.renderAs(),S=M?C?.getPluginsForField(M.key)?.[0]?.sizingStrategy:void 0}catch{p=void 0,S=void 0}o?.(Y(p,S),p),c.dataset.malloyRenderAs=p??"unknown",c.dataset.malloySizing=Y(p,S);const m=Ne(p,v);c.style.minWidth=m===void 0?"":`${m}px`,d.style.overflowX=m===void 0?"":"auto",d.style.overflowY=m===void 0?"":"hidden",b.render(c);const re=i.current;try{if(re){const C=Me(b,re.canDrill),M=()=>{try{ze(h,C)}catch(s){console.warn("Drill affordance skipped:",s)}};M();const ae=s=>{const u=s.querySelector(".cell-content")??s,z=u.getBoundingClientRect();u.dispatchEvent(new MouseEvent("click",{bubbles:!0,cancelable:!0,view:u.ownerDocument.defaultView,clientX:Math.round(z.left+z.width/2),clientY:Math.round(z.bottom)}))},oe=s=>s.target?.closest?.(`.${w}`)??null,be=(s,u)=>{ie(s,u)?.focus()};I=s=>{const u=oe(s);if(!u)return;const z=s.key==="ArrowDown"?1:s.key==="ArrowUp"?-1:s.key==="Home"?"first":s.key==="End"?"last":void 0;if(z!==void 0){s.preventDefault(),be(u,z);return}s.key==="Enter"?(s.preventDefault(),ae(u)):s.key===" "&&s.preventDefault()},q=s=>{if(s.key!==" ")return;const u=oe(s);u&&(s.preventDefault(),ae(u))},N=s=>{const u=s.target?.closest?.(`.${w}`);u&&ie(u,0)},h.addEventListener("keydown",I),h.addEventListener("keyup",q),h.addEventListener("focusin",N),C.size>0&&typeof MutationObserver<"u"&&(L=new MutationObserver(()=>{cancelAnimationFrame(W),W=requestAnimationFrame(M)}),L.observe(h,{childList:!0,subtree:!0}))}}catch(C){console.warn("Drill affordance skipped:",C)}b.onReady(te),A=setTimeout(te,1e4)}catch(m){console.error("Error rendering visualization:",m),j?.disconnect(),_?.disconnect(),L?.disconnect(),b.remove(),b=void 0,h.parentNode===d&&d.removeChild(h)}})(),()=>{H=!0,j?.disconnect(),_?.disconnect(),F&&clearTimeout(F),A&&clearTimeout(A),c&&k.current?.node!==c&&(I&&c.removeEventListener("keydown",I),q&&c.removeEventListener("keyup",q),N&&c.removeEventListener("focusin",N),L?.disconnect(),cancelAnimationFrame(W),b?.remove(),c.parentNode===d&&d.removeChild(c),d.style.position="")}},[e,y,g,a,o,D,V,J,Z]),O.jsx("div",{ref:l,...n?{[T]:""}:{},style:{width:"100%",height:t?`${t}px`:"400px"}})}function Ye(e){return typeof window>"u"?O.jsx(ne,{sx:{width:"100%",height:e.height?`${e.height}px`:"100%",display:"flex",alignItems:"center",justifyContent:"center",color:"text.secondary"},children:"Loading..."}):O.jsx(f.Suspense,{fallback:O.jsx(ne,{sx:{width:"100%",height:e.height?`${e.height}px`:"100%",display:"flex",alignItems:"center",justifyContent:"center",color:"text.secondary"},children:"Loading visualization..."}),children:O.jsx(Xe,{...e})})}const Je=Object.freeze(Object.defineProperty({__proto__:null,default:Ye},Symbol.toStringTag,{value:"Module"}));export{w as D,Ye as R,_e as a,Fe as b,Je as c,Me as d,ze as m,Re as r};