@metabase/custom-viz 0.0.1-alpha.1 → 0.0.1-alpha.3

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/README.md CHANGED
@@ -1,53 +1,50 @@
1
1
  # @metabase/custom-viz
2
2
 
3
- CLI for creating and bundling custom visualizations for Metabase.
3
+ CLI and type definitions for creating custom visualizations for Metabase.
4
4
 
5
- ## Prerequisites
5
+ ## Getting Started
6
6
 
7
- - Node.js >= 20
8
- - npm >= 10
9
-
10
- ## Development
7
+ ### 1. Scaffold a new visualization
11
8
 
12
9
  ```bash
13
- # Install dependencies
14
- npm install
10
+ npx @metabase/custom-viz init my-viz
11
+ ```
15
12
 
16
- # Build in watch mode
17
- npm run dev
13
+ This creates a new directory with everything you need:
18
14
 
19
- # Production build
20
- npm run build
15
+ ```
16
+ my-viz/
17
+ src/index.tsx # Your visualization code
18
+ package.json
19
+ vite.config.ts
20
+ tsconfig.json
21
21
  ```
22
22
 
23
- ## Linting & Formatting
23
+ ### 2. Install dependencies and start developing
24
24
 
25
25
  ```bash
26
- # Lint
27
- npm run lint
28
-
29
- # Format code
30
- npm run format
31
-
32
- # Check formatting
33
- npm run format:check
26
+ cd my-viz
27
+ npm install
28
+ npm run dev # Watch mode — rebuilds on changes
34
29
  ```
35
30
 
36
- ## Publishing
37
-
38
- This package uses [np](https://github.com/sindresorhus/np) for publishing.
31
+ ### 3. Build for production
39
32
 
40
33
  ```bash
41
- npm run release
34
+ npm run build
42
35
  ```
43
36
 
44
- `np` will guide you through version bumping, run the build, and publish to npm.
37
+ The build output will be in the `dist/` directory, ready to be loaded into Metabase.
45
38
 
46
39
  ## Project Structure
47
40
 
48
41
  ```
49
42
  src/
50
- cli.ts # CLI entry point (commander)
51
- dist/ # Build output
52
- vite.config.ts # Vite build configuration
43
+ cli.ts # CLI entry point (commander)
44
+ index.ts # Library entry point (type exports)
45
+ templates.ts # Scaffolding templates
46
+ templates/ # Template files for `init` command
47
+ types/ # Custom visualization type definitions
48
+ dist/ # Build output
49
+ vite.config.ts # Vite build configuration
53
50
  ```
package/dist/cli.js CHANGED
@@ -1,39 +1,54 @@
1
1
  #!/usr/bin/env node
2
- import { existsSync as e } from "node:fs";
3
- import { mkdir as t, writeFile as n } from "node:fs/promises";
4
- import { join as r } from "node:path";
5
- import { Command as i } from "commander";
6
- //#region src/templates/.gitignore?raw
7
- var a = "node_modules/\n.DS_Store\n# dist must be committed\n", o = "import type {\n CreateCustomVisualization,\n CustomStaticVisualizationProps,\n CustomVisualizationProps,\n} from \"@metabase/custom-viz\";\n\ntype Settings = {\n threshold?: number;\n};\n\nconst createVisualization: CreateCustomVisualization<Settings> = () => {\n return {\n id: \"__CUSTOM_VIZ_NAME__\",\n getName: () => \"__CUSTOM_VIZ_NAME__\",\n minSize: { width: 1, height: 1 },\n defaultSize: { width: 2, height: 2 },\n isSensible({ cols, rows }) {\n return cols.length === 1 && rows.length === 1 && typeof rows[0][0] === \"number\";\n },\n checkRenderable(series, settings) {\n if (series.length !== 1) {\n throw new Error(\"Only 1 series is supported\");\n }\n\n const [\n {\n data: { cols, rows },\n },\n ] = series;\n\n if (cols.length !== 1) {\n throw new Error(\"Query results should only have 1 column\");\n }\n\n if (rows.length !== 1) {\n throw new Error(\"Query results should only have 1 row\");\n }\n\n if (typeof rows[0][0] !== \"number\") {\n throw new Error(\"Result is not a number\");\n }\n\n if (typeof settings.threshold !== \"number\") {\n throw new Error(\"Threshold setting is not set\");\n }\n },\n settings: {\n threshold: {\n id: \"1\",\n title: \"Threshold\",\n widget: \"number\",\n getDefault() {\n return 0;\n },\n getProps() {\n return {\n options: {\n isInteger: false,\n isNonNegative: false,\n },\n placeholder: \"Set threshold\",\n };\n },\n },\n },\n VisualizationComponent,\n StaticVisualizationComponent,\n };\n};\n\nconst VisualizationComponent = (props: CustomVisualizationProps<Settings>) => {\n const { height, series, settings, width } = props;\n const { threshold } = settings;\n const value = series[0].data.rows[0][0];\n\n if (typeof value !== \"number\" || typeof threshold !== \"number\") {\n throw new Error(\"Value and threshold need to be numbers\");\n }\n\n const emoji = value >= threshold ? \"👍\" : \"👎\";\n\n return (\n <div\n style={{\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n width,\n height,\n fontSize: \"10rem\",\n }}\n >\n {emoji}\n </div>\n );\n};\n\nconst StaticVisualizationComponent = (\n props: CustomStaticVisualizationProps<Settings>,\n) => {\n const width = 540;\n const height = 360;\n const { series, settings } = props;\n const { threshold } = settings;\n const value = series[0].data.rows[0][0];\n\n if (typeof value !== \"number\" || typeof threshold !== \"number\") {\n throw new Error(\"Value and threshold need to be numbers\");\n }\n\n const emoji =\n value >= threshold ? (\n <img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKAAAACgCAYAAACLz2ctAAAA0mVYSWZJSSoACAAAAAoAAAEEAAEAAACgAAAAAQEEAAEAAACgAAAAAgEDAAMAAACGAAAAEgEDAAEAAAABAAAAGgEFAAEAAACMAAAAGwEFAAEAAACUAAAAKAEDAAEAAAACAAAAMQECAA8AAACcAAAAMgECABQAAACsAAAAaYcEAAEAAADAAAAAAAAAAAgACAAIACwBAAABAAAALAEAAAEAAABHSU1QIDMuMC4wLVJDMQAAMjAyNjowMzoxOCAxNjoyNzo1OQABAAGgAwABAAAAAQAAAAAAAAD8VR8dAAABhWlDQ1BJQ0MgcHJvZmlsZQAAeJx9kb9Lw0AcxV9bpSoVQTuIPyBDdbKLijjWKhShQqgVWnUwufQXNGlIUlwcBdeCgz8Wqw4uzro6uAqC4A8Q/wBxUnSREr+XFFrEeHDch3f3HnfvAH+9zFSzIwaommWkEnEhk10Vgq8IoB/dGMaoxEx9ThST8Bxf9/Dx9S7Ks7zP/Tl6lZzJAJ9AHGO6YRFvEM9sWjrnfeIwK0oK8TnxhEEXJH7kuuzyG+eCw36eGTbSqXniMLFQaGO5jVnRUImniSOKqlG+P+OywnmLs1qusuY9+QtDOW1lmes0R5DAIpYgQoCMKkoow0KUVo0UEynaj3v4hxy/SC6ZXCUwciygAhWS4wf/g9/dmvmpSTcpFAc6X2z7YwwI7gKNmm1/H9t24wQIPANXWstfqQOzn6TXWlrkCOjbBi6uW5q8B1zuAINPumRIjhSg6c/ngfcz+qYsMHAL9Ky5vTX3cfoApKmr5A1wcAiMFyh73ePdXe29/Xum2d8PeZVyqWZhNiAAAA16aVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA0LjQuMC1FeGl2MiI+CiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiCiAgICB4bWxuczpHSU1QPSJodHRwOi8vd3d3LmdpbXAub3JnL3htcC8iCiAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgeG1wTU06RG9jdW1lbnRJRD0iZ2ltcDpkb2NpZDpnaW1wOjUyMGY3ZWIzLTNiNjktNDlkMy04NjZjLWIwOWI4YzQ4MzgwZSIKICAgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozZDFlNjVmZS1kYWQ5LTQ4MjQtYmE5MS1hNGMyYzhmOWNjMmIiCiAgIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo4YzU1Zjg3MC02NjE4LTQ1MjAtYTE2Yy0wZmVlYWJjODE4ZTIiCiAgIEdJTVA6QVBJPSIzLjAiCiAgIEdJTVA6UGxhdGZvcm09IkxpbnV4IgogICBHSU1QOlRpbWVTdGFtcD0iMTc3MzgyNjA4MDI4Mzg3NSIKICAgR0lNUDpWZXJzaW9uPSIzLjAuMC1SQzEiCiAgIGRjOkZvcm1hdD0iaW1hZ2UvcG5nIgogICB0aWZmOk9yaWVudGF0aW9uPSIxIgogICB4bXA6Q3JlYXRvclRvb2w9IkdJTVAiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjY6MDM6MThUMTY6Mjc6NTkrMDc6MDAiCiAgIHhtcDpNb2RpZnlEYXRlPSIyMDI2OjAzOjE4VDE2OjI3OjU5KzA3OjAwIj4KICAgPHhtcE1NOkhpc3Rvcnk+CiAgICA8cmRmOlNlcT4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0OmNoYW5nZWQ9Ii8iCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6YTc4M2Q4MzktMTY2Yy00MTM2LWI3ZDgtMWJlNGU1ZTcxNjFjIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJHSU1QIDMuMC4wLVJDMSAoTGludXgpIgogICAgICBzdEV2dDp3aGVuPSIyMDI2LTAzLTE4VDE2OjI4OjAwKzA3OjAwIi8+CiAgICA8L3JkZjpTZXE+CiAgIDwveG1wTU06SGlzdG9yeT4KICA8L3JkZjpEZXNjcmlwdGlvbj4KIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAKPD94cGFja2V0IGVuZD0idyI/PlfBSK4AAAAGYktHRAAAAEgAkHfF9EMAAAAJcEhZcwAALiMAAC4jAXilP3YAAAAHdElNRQfqAxIJHAAvHRFSAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBHSU1QV4EOFwAACzdJREFUeNrtnXuQ1lUZxz/LLrHcZLnIgoYNN02oNGAyCUqUS2o6aRenZlKwEawxGa0ZJhswrZFwYsCKGqapyHEatYYIzUshTWqCFGCajFxWwEkEgWW5yHXh7Y/z21iWRZ7zvuf3vr/L9zNzZvjj8J7fec53z/08TxWiFGqBScBEYCQwCKgDCkAjsAFYC7wAPA/slMlECHoCDwC7IrFZ0olIjPcDQ2RCUSw3A7s9hNdeOg48BlwgcwornYDflii8tmk/MEWmFWejM/BcYPG1TrNlYnEmOgBLYhRfS5olU4v2uKcM4mtZpFwtc4vWXAwcKZMAC8A2oFueDFwtjb0viyIRlovuwHu4fUORcy6JhsVCmdO2PHUMHaSzMzIFqPLI/wTuVKQP0BsYA8yMVs/NHr/TH7hK5hdvGXusZmDqWX6rHvidRy/4oMyfbwYSfg+vCnjG+JsvqgnyzXVGoeyNFg5WJhp/9zDu5EVzwJxiPaNdjjtOs7ICdw58NjrhbtdIgDmlvzHfG56/ux/4jzHvaAkwv9Qb820v4rdXGPNdJgHml37GfO/EKMDhEqAEeDZ2xCjAoXlYiEiA7dPXmO/dIn57U7R6Phs1kQglQM0Bgw7BBdxbEQvDJMD8UWcc+o4ae7L2sApwuASYP87zmP8VYhbgQAkwfwyKcfhtYb0x3/kSYP6wPplsKKGMjRKgBFhqD/hmCWVsCTwdkABz2ANuKqGMPdjuCHYHzpEA1QOGHoJbXHdY6C8B5odqj5VnQ4ll7Tbm6y4B5ocPAR8w5DtY4ioYnF8ZC90kwPwwwmMBUiiTALtKgPnh48Z86wKUdUA9oATYFust5NUByrL2oOoB1QOexpoAZZ0w5uskAeaDAdiuYRUCCdDaAzZLgPlglDHfFux7eCF6QAkwJ4wv4/zPpwc8JgHmg0llnP8BdFQPKAG2MAQYbMz7UqAye0qAEmALnzXmOwisDFRmb2O+fRJg9rnWmO9FnMPKEPQw5tstAWabQcAEY95nApbbSwIUAHOxe8IaFLDcQ8Yyu6qJsks33J6eRQivBiy3DruXrEyT9yH42x6r0V8HLNe64lZsuQzTN1phWnqi9zyEauEmY7nPqwfMLj/Eftv4Ydw7jlCU4+WdBJhghuMXo+3nMay8LbwpAWaPauCXOOc/FpYCrwX+hnK8vBMJZQb2bZdmwvtnqcIeZ/gTaq5sMQz7/lsBWBjDN1yIPXZcnZosO9QAL3uI7wDxvMn9mrH8DXlolDzNAWd6DmlzKf3pZXtYv2GN+ozsMD6az1l7vwbiOwJbZfyG76jZssFA3IG+z8IjrhAJnXDHa5bvuDIv86Is0wN4EvvNE4B5hLt02pax2F+5VQM34B5LDcB5yuoZLUx6tEot74abOPWa/8HoD28Hzpf11mhbZxMuVske9U3x/3E9i1+o1HVAbYzfNJfyh389U9qIC6D4Lez7ksJjcfWwZ4McI/7gMOsSJMC2aT3wY8oboDuTVOGOznwb4N4yzEULKUgncBdvPyIpFSe++UUY/ekybEl9MyUCbD0izMH+ek/DLu6M19fQW7A/ECqFpSkTYOvYxf0kr/enFni0COPuBy4p0zduT6kAW05lLpDM2qcv8I8ijNqMC0xdLvanWIAF3NWwesntVC4FNhdp0Oll/tZ/plyAhWh/tFayOzmpP1SkIe+rwPdOy4AAC8BPQ68a00Y9sAD4QpH/fz5wV4UWSX8Erjfm3xb17m/hTjG2Av/FxadrnZqi/B051ZtqXTQ9OTfaAhoKfBjnhLNXCfUoABOBZXncYrkVv3PdtmlBhf/oanC3cna2+qaDuBjCC6NefQzxxwYZAtwGLC5ybvoG2T/GPYXLgRdKHDrmJKjHr45EMCgB+2x1uAdaRz3teXsehPcxYEmAectMTZnPyoRoSPfZP63OoiFaboEsDyC8I8BkacvMGM+e8HNZEt044GfR5DvEam0n8Glpypu7PWz8+7QvKsZGogt9SrAGu/sLcXq7vGS0cxMpPCuuwp1ArI5xn6qTdFQSEz3sPTpNFRsSYDV7prQLuFHaCdZJWE+YpqWlUlNxTx3jEN8j2GJ8CDu/MNp+Xhr+mmbHJLwG7N7thR+3G9tgUdIrsiAG4e0A7sQWWlUUh9WFXKJXwtMDC68RmEXGI0gmhMke059EMhb/450zpdejya78JZePHxnb5qEkfnw17u1pKaLbDfwGuIp03tpJO68a2+nuNHff7Z1eLIz2ofQYpnJc4dFmVySxApvwfxD+JXJ2xSeh1GC/vX0Yu5vjsnEp/nf0dHKRHB7waLs/J7EC93pUYJ7aO1Hc5tl53JzESvwN+4UBDbnJmrcf9xDftqSOXG8bK3CT2jy14ku0/0LrS7U+avdEMKUI8a0jwadQ1kpob6/yVOHvwq4A3JLkSlkrIZJBZ+CvngJ8XAIUoUW4DD/PWf0kQBGSLh47GAXgGxKgCE0/7FGcnpQARRxYb8E0kdB3wRJgurnIow2HlVpYB9lbtGE9zhGSBQlQxMK/jPmGSoAiDl4x5uslAYo42G7MVycBijhoMubrKQGKSgqwhwQo4qCLMd9BCVDEgXVobZIARRxYFxd7JUARB9YN5kYJUMTBCGO+t5P48ToLTjddce99LW34KQlQhGYy9lh750iAIiRVwEpj+60OUaDmgKI104HLjHkTG6pLPWB6h16f55kjJUBRKfG9nuTKSIDpopiH6V+XAEWoRUcxD9MHS4AiFL5vghPtF0YCTK8IfbwjPCUBitD4PEzfJgGKOBhibLvjJNi3owSYbqzu9YKEzNBJiGhNDTa/fydwDookQBGUkUZNvIMLQiQBiqCMM+bbGqpACVC01sKtxryvSYAiNJOwu9pYluSKaBWcPqqAVdgvovaWAEVIvkLKoyNJgOmlD+5kw9puN0iAIuTQ+yePNttMCqKYHjNWplbtX3Huw+8WzNQ0VGqPsTKj1P4VxTcwYQMJjo7UmrXGCi2UBirGZPxvQV+Xlsr9ymM5f420kArxLU1TBa/3qNhhYBraEC8Xxbz/2Amcl6ZKdgYO4B99cRZwLXBhWuYaKVzxFvP+I5VhdRcUUdG2w/MW4DngJ7jI3BerpwzSOfi+/7g6jRWtB/aVKML20l7ce4Q7o55SxC/COWmt6HdjEGDbtAn4AS66j7DTBfi70cbPprWSHfDbZS81rYyGas0fbUygjE6IKkV34N9lFGEBd1v3eyTUdViCGG6058a0V7RntJgolDntAmZg9/ieN6zbZauyUNmO0cr4RAWEuAX4ovR2GjPJyPUrH0YCKyogwgLu0fVHpbv/z88bjHZ7MIuVvwZ4HLsv4lDpGDCfADHOUs7nPWz25Swboi5ajc0AHsWFCW0sgxDfxV0tqs6h+Opwr9qsBwL1efwL7QVcDtwBLMI5RIxDiGuBz+TIrp3wc0L0F81WTnI+7tngH7C7j7Cm5cB43HlpVjkXu/OhlvRVya59ekRifDmwEDcC9xAgFH2CqMZt0G/3tMWGnE5RvBkNLIlhi2cz8AhwF85jwOBoCEsDnYFPAvd7rHYr0vtlacgZBcyOhtI4acRFiWwG9ifMBrXRIqOe0tynPY0uCxfNjVHvVVAqKjUCAySj0oeg7+MCKktU9nQUuFLyCccgYLGEZfZ6OlmSiYdxwCsS2RnTEZx7DhHzdsQ03HUtie5k2q5ht/wrxTuwH0dlOS2ONqhFBajB3Yl7ItpSyZPwVuFeIIqE0Be4BXgMu2uRtKWDuBtIidrfq5L22rXJUGBElC4CPog7i07TzZB9uMsWa3BHlk+RvI1zCbCI+WNn3Fl0B9ypQ5JseAh3SrMn+rcQQojE8j/2Mm8tOLYIDgAAAABJRU5ErkJggg==\" />\n ) : (\n <img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKAAAACgCAYAAACLz2ctAAAA0mVYSWZJSSoACAAAAAoAAAEEAAEAAACgAAAAAQEEAAEAAACgAAAAAgEDAAMAAACGAAAAEgEDAAEAAAABAAAAGgEFAAEAAACMAAAAGwEFAAEAAACUAAAAKAEDAAEAAAACAAAAMQECAA8AAACcAAAAMgECABQAAACsAAAAaYcEAAEAAADAAAAAAAAAAAgACAAIACwBAAABAAAALAEAAAEAAABHSU1QIDMuMC4wLVJDMQAAMjAyNjowMzoxOCAxNjoyODowOAABAAGgAwABAAAAAQAAAAAAAABiEXj2AAABhWlDQ1BJQ0MgcHJvZmlsZQAAeJx9kb9Lw0AcxV9bpSoVQTuIPyBDdbKLijjWKhShQqgVWnUwufQXNGlIUlwcBdeCgz8Wqw4uzro6uAqC4A8Q/wBxUnSREr+XFFrEeHDch3f3HnfvAH+9zFSzIwaommWkEnEhk10Vgq8IoB/dGMaoxEx9ThST8Bxf9/Dx9S7Ks7zP/Tl6lZzJAJ9AHGO6YRFvEM9sWjrnfeIwK0oK8TnxhEEXJH7kuuzyG+eCw36eGTbSqXniMLFQaGO5jVnRUImniSOKqlG+P+OywnmLs1qusuY9+QtDOW1lmes0R5DAIpYgQoCMKkoow0KUVo0UEynaj3v4hxy/SC6ZXCUwciygAhWS4wf/g9/dmvmpSTcpFAc6X2z7YwwI7gKNmm1/H9t24wQIPANXWstfqQOzn6TXWlrkCOjbBi6uW5q8B1zuAINPumRIjhSg6c/ngfcz+qYsMHAL9Ky5vTX3cfoApKmr5A1wcAiMFyh73ePdXe29/Xum2d8PeZVyqWZhNiAAAA16aVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA0LjQuMC1FeGl2MiI+CiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiCiAgICB4bWxuczpHSU1QPSJodHRwOi8vd3d3LmdpbXAub3JnL3htcC8iCiAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgeG1wTU06RG9jdW1lbnRJRD0iZ2ltcDpkb2NpZDpnaW1wOmI5OGQ1OTk1LTRkZjktNDdkMS1hMGRmLTJjYWJhOTU2NjFjZSIKICAgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo4MzNlNzUzYS0zZDhmLTRhNTAtODA1NC0wNmQ0N2JhNzQ1NWMiCiAgIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoyYzJiM2I3Zi04OTU0LTQyZTgtOWNkOS1lOTc3Y2ExZmMwMGYiCiAgIEdJTVA6QVBJPSIzLjAiCiAgIEdJTVA6UGxhdGZvcm09IkxpbnV4IgogICBHSU1QOlRpbWVTdGFtcD0iMTc3MzgyNjA4OTM4NzIzMiIKICAgR0lNUDpWZXJzaW9uPSIzLjAuMC1SQzEiCiAgIGRjOkZvcm1hdD0iaW1hZ2UvcG5nIgogICB0aWZmOk9yaWVudGF0aW9uPSIxIgogICB4bXA6Q3JlYXRvclRvb2w9IkdJTVAiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjY6MDM6MThUMTY6Mjg6MDgrMDc6MDAiCiAgIHhtcDpNb2RpZnlEYXRlPSIyMDI2OjAzOjE4VDE2OjI4OjA4KzA3OjAwIj4KICAgPHhtcE1NOkhpc3Rvcnk+CiAgICA8cmRmOlNlcT4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0OmNoYW5nZWQ9Ii8iCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6NDM0NWQ1MjktYzlmYi00NjQ5LTlmZDctYmVkMDNiYjMzNjUzIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJHSU1QIDMuMC4wLVJDMSAoTGludXgpIgogICAgICBzdEV2dDp3aGVuPSIyMDI2LTAzLTE4VDE2OjI4OjA5KzA3OjAwIi8+CiAgICA8L3JkZjpTZXE+CiAgIDwveG1wTU06SGlzdG9yeT4KICA8L3JkZjpEZXNjcmlwdGlvbj4KIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAKPD94cGFja2V0IGVuZD0idyI/PkUgIFwAAAAGYktHRAAAAEgAkHfF9EMAAAAJcEhZcwAALiMAAC4jAXilP3YAAAAHdElNRQfqAxIJHAlWwan2AAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBHSU1QV4EOFwAACwtJREFUeNrtnXuwVlUZh5+DAnI9ykWBJC9cdHAgRSUh1Kwhi6KG0TTorpjTDScNRm1Kx/5wqhHTAjRMssbRaaBxiDIgyUKHSASV0FIUUFFB4CBXhQNff6x1GOZ08Lzr+9be3778npk1wMzi23ut97fXfb1vA6ItOgBDgHP9nwOBk/2fXYDjfb4TMvbe7wI7gCZgC7AaeBpYAbwks2abYcB0YCmwE6gULD0L3AR8UKbODgOBHwHrCii4o6X9wL0SYn0ZAywAmkskvNbpPeAHwLGSQ3qMAh4rsejaSsuB0ySNZOnvu52DElyb6S1gpGQSn+P8wHuXRNZu2gmMlWTiMaFkk4sYaatfdhI1MBR4VGKqOv0X6CEZhdML+Jmf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\" />\n );\n\n return (\n <div\n style={{\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n width,\n height,\n fontSize: \"10rem\",\n }}\n >\n {emoji}\n </div>\n );\n};\n\nexport default createVisualization;\n", s = "{\n \"name\": \"__CUSTOM_VIZ_NAME__\",\n \"version\": \"0.0.1\",\n \"private\": true,\n \"type\": \"module\",\n \"scripts\": {\n \"build\": \"vite build\",\n \"dev\": \"vite build --watch & vite preview\",\n \"type-check\": \"tsc --noEmit\"\n },\n \"devDependencies\": {\n \"@metabase/custom-viz\": \"^0.0.1\",\n \"@types/react\": \"^19.2.14\",\n \"react\": \"^19.1.0\",\n \"typescript\": \"^5.9.3\",\n \"vite\": \"^8.0.0\"\n }\n}\n", c = "{\n \"compilerOptions\": {\n \"target\": \"ES2020\",\n \"module\": \"ESNext\",\n \"moduleResolution\": \"bundler\",\n \"jsx\": \"react-jsx\",\n \"strict\": true,\n \"esModuleInterop\": true,\n \"skipLibCheck\": true,\n \"outDir\": \"dist\",\n \"declaration\": true\n },\n \"include\": [\"src\"]\n}\n", l = "import { resolve } from \"path\";\nimport { createServer } from \"http\";\nimport { defineConfig } from \"vite\";\n\n/**\n * Vite plugin that replaces `react` and `react/jsx-runtime` imports with\n * virtual modules that read from Metabase's `window.__METABASE_VIZ_API__`.\n *\n * This is necessary because the ES module output format cannot use\n * `output.globals`, and bare `import 'react'` would fail in the browser.\n */\nfunction metabaseVizExternals() {\n const VIRTUAL_REACT = \"\\0virtual:react\";\n const VIRTUAL_JSX_RUNTIME = \"\\0virtual:react/jsx-runtime\";\n\n return {\n name: \"metabase-viz-externals\",\n enforce: \"pre\" as const,\n\n resolveId(source) {\n if (source === \"react\") {\n return VIRTUAL_REACT;\n }\n if (source === \"react/jsx-runtime\") {\n return VIRTUAL_JSX_RUNTIME;\n }\n return null;\n },\n\n load(id) {\n if (id === VIRTUAL_REACT) {\n return [\n \"const React = window.__METABASE_VIZ_API__.React;\",\n \"export default React;\",\n \"export const { useState, useEffect, useRef, useCallback, useMemo, useReducer, useContext, createElement, Fragment } = React;\",\n ].join(\"\\n\");\n }\n if (id === VIRTUAL_JSX_RUNTIME) {\n return [\n \"const jsxRuntime = window.__METABASE_VIZ_API__.jsxRuntime;\",\n \"export const { jsx, jsxs, Fragment } = jsxRuntime;\",\n ].join(\"\\n\");\n }\n return null;\n },\n };\n}\n\nconst NOTIFY_PORT = 5175;\n\n/**\n * Vite plugin that starts a tiny SSE server and sends a \"reload\" event\n * to all connected clients after each rebuild completes.\n * Metabase's frontend connects to this to live-reload the custom viz.\n */\nfunction metabaseNotifyReload() {\n const clients = new Set<import(\"http\").ServerResponse>();\n let server: ReturnType<typeof createServer> | null = null;\n\n return {\n name: \"metabase-notify-reload\",\n\n buildStart() {\n if (server) {\n return;\n }\n server = createServer((req, res) => {\n res.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"Access-Control-Allow-Origin\": \"*\",\n });\n clients.add(res);\n req.on(\"close\", () => clients.delete(res));\n });\n server.listen(NOTIFY_PORT, () => {\n console.log(\n `[metabase-notify] SSE server listening on http://localhost:${NOTIFY_PORT}`,\n );\n });\n },\n\n closeBundle() {\n for (const client of clients) {\n client.write(\"data: reload\\n\\n\");\n }\n console.log(\n `[metabase-notify] Build complete, notified ${clients.size} client(s)`,\n );\n },\n };\n}\n\nconst isWatch = process.argv.includes(\"--watch\");\n\nexport default defineConfig({\n plugins: [metabaseVizExternals(), ...(isWatch ? [metabaseNotifyReload()] : [])],\n build: {\n outDir: \"dist\",\n lib: {\n entry: resolve(__dirname, \"src/index.tsx\"),\n formats: [\"iife\"],\n fileName: () => \"index.js\",\n name: \"__customVizPlugin__\",\n },\n },\n preview: {\n port: 5174,\n host: true,\n cors: true,\n },\n});\n", u = "__CUSTOM_VIZ_NAME__";
8
- function d(e, t) {
9
- return e.split(u).join(t);
10
- }
11
- function f(e) {
12
- return d(s, e);
13
- }
14
- function p() {
15
- return l;
2
+ import { existsSync as e, readFileSync as t } from "node:fs";
3
+ import { mkdir as n, writeFile as r } from "node:fs/promises";
4
+ import { dirname as i, join as a } from "node:path";
5
+ import { Command as o } from "commander";
6
+ import { fileURLToPath as s } from "node:url";
7
+ //#region package.json
8
+ var c = "0.0.1-alpha.3", l = "node_modules/\n.DS_Store\n# dist must be committed\n", u = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"/>\n <line x1=\"3\" y1=\"9\" x2=\"21\" y2=\"9\"/>\n <line x1=\"9\" y1=\"21\" x2=\"9\" y2=\"9\"/>\n</svg>\n", d = "import type {\n CreateCustomVisualization,\n CustomStaticVisualizationProps,\n CustomVisualizationProps,\n} from \"@metabase/custom-viz\";\n\ntype Settings = {\n threshold?: number;\n};\n\nconst createVisualization: CreateCustomVisualization<Settings> = ({\n getAssetUrl,\n}) => {\n return {\n id: \"__CUSTOM_VIZ_NAME__\",\n getName: () => \"__CUSTOM_VIZ_NAME__\",\n minSize: { width: 1, height: 1 },\n defaultSize: { width: 2, height: 2 },\n isSensible({ cols, rows }) {\n return cols.length === 1 && rows.length === 1 && typeof rows[0][0] === \"number\";\n },\n checkRenderable(series, settings) {\n if (series.length !== 1) {\n throw new Error(\"Only 1 series is supported\");\n }\n\n const [\n {\n data: { cols, rows },\n },\n ] = series;\n\n if (cols.length !== 1) {\n throw new Error(\"Query results should only have 1 column\");\n }\n\n if (rows.length !== 1) {\n throw new Error(\"Query results should only have 1 row\");\n }\n\n if (typeof rows[0][0] !== \"number\") {\n throw new Error(\"Result is not a number\");\n }\n\n if (typeof settings.threshold !== \"number\") {\n throw new Error(\"Threshold setting is not set\");\n }\n },\n settings: {\n threshold: {\n id: \"1\",\n title: \"Threshold\",\n widget: \"number\",\n getDefault() {\n return 0;\n },\n getProps() {\n return {\n options: {\n isInteger: false,\n isNonNegative: false,\n },\n placeholder: \"Set threshold\",\n };\n },\n },\n },\n VisualizationComponent: makeVisualizationComponent(getAssetUrl),\n StaticVisualizationComponent: makeStaticVisualizationComponent(getAssetUrl),\n };\n};\n\nconst makeVisualizationComponent = (getAssetUrl: (path: string) => string) => (props: CustomVisualizationProps<Settings>) => {\n const { height, series, settings, width } = props;\n const { threshold } = settings;\n const value = series[0].data.rows[0][0];\n\n if (typeof value !== \"number\" || typeof threshold !== \"number\") {\n throw new Error(\"Value and threshold need to be numbers\");\n }\n\n const emoji = value >= threshold ? \"👍\" : \"👎\";\n\n return (\n <div\n style={{\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n width,\n height,\n fontSize: \"10rem\",\n }}\n >\n {emoji}\n </div>\n );\n};\n\nconst makeStaticVisualizationComponent = (getAssetUrl: (path: string) => string) => (\n props: CustomStaticVisualizationProps<Settings>,\n) => {\n const width = 540;\n const height = 360;\n const { series, settings } = props;\n const { threshold } = settings;\n const value = series[0].data.rows[0][0];\n\n if (typeof value !== \"number\" || typeof threshold !== \"number\") {\n throw new Error(\"Value and threshold need to be numbers\");\n }\n\n const emoji =\n value >= threshold ? (\n <img src={getAssetUrl(\"thumbs-up.png\")} />\n ) : (\n <img src={getAssetUrl(\"thumbs-down.png\")} />\n );\n\n return (\n <div\n style={{\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n width,\n height,\n fontSize: \"10rem\",\n }}\n >\n {emoji}\n </div>\n );\n};\n\nexport default createVisualization;\n", f = "{\n \"name\": \"__CUSTOM_VIZ_NAME__\",\n \"icon\": \"icon.svg\",\n \"assets\": [\"icon.svg\", \"thumbs-up.png\", \"thumbs-down.png\"],\n \"metabase\": {\n \"version\": \">=59\"\n }\n}\n", p = "{\n \"name\": \"__CUSTOM_VIZ_NAME__\",\n \"version\": \"0.0.1\",\n \"private\": true,\n \"type\": \"module\",\n \"scripts\": {\n \"build\": \"vite build\",\n \"dev\": \"vite build --watch & vite preview\",\n \"type-check\": \"tsc --noEmit\"\n },\n \"devDependencies\": {\n \"@metabase/custom-viz\": \"__CUSTOM_VIZ_VERSION__\",\n \"@types/react\": \"^19.2.14\",\n \"react\": \"^19.1.0\",\n \"typescript\": \"^5.9.3\",\n \"vite\": \"^8.0.0\"\n }\n}\n", m = "{\n \"compilerOptions\": {\n \"target\": \"ES2020\",\n \"module\": \"ESNext\",\n \"moduleResolution\": \"bundler\",\n \"jsx\": \"react-jsx\",\n \"strict\": true,\n \"esModuleInterop\": true,\n \"skipLibCheck\": true,\n \"outDir\": \"dist\",\n \"declaration\": true\n },\n \"include\": [\"src\"]\n}\n", h = "import { resolve } from \"path\";\nimport { createServer } from \"http\";\nimport { watch, cpSync } from \"fs\";\nimport { defineConfig } from \"vite\";\n\n/**\n * Vite plugin that replaces `react` and `react/jsx-runtime` imports with\n * virtual modules that read from Metabase's `window.__METABASE_VIZ_API__`.\n *\n * This is necessary because the ES module output format cannot use\n * `output.globals`, and bare `import 'react'` would fail in the browser.\n */\nfunction metabaseVizExternals() {\n const VIRTUAL_REACT = \"\\0virtual:react\";\n const VIRTUAL_JSX_RUNTIME = \"\\0virtual:react/jsx-runtime\";\n const VIRTUAL_CUSTOM_VIZ = \"\\0virtual:@metabase/custom-viz\";\n\n return {\n name: \"metabase-viz-externals\",\n enforce: \"pre\" as const,\n\n resolveId(source) {\n if (source === \"react\") {\n return VIRTUAL_REACT;\n }\n if (source === \"react/jsx-runtime\") {\n return VIRTUAL_JSX_RUNTIME;\n }\n if (source === \"@metabase/custom-viz\") {\n return VIRTUAL_CUSTOM_VIZ;\n }\n return null;\n },\n\n load(id) {\n if (id === VIRTUAL_REACT) {\n return [\n \"const React = window.__METABASE_VIZ_API__.React;\",\n \"export default React;\",\n \"export const { useState, useEffect, useRef, useCallback, useMemo, useReducer, useContext, createElement, Fragment } = React;\",\n ].join(\"\\n\");\n }\n if (id === VIRTUAL_JSX_RUNTIME) {\n return [\n \"const jsxRuntime = window.__METABASE_VIZ_API__.jsxRuntime;\",\n \"export const { jsx, jsxs, Fragment } = jsxRuntime;\",\n ].join(\"\\n\");\n }\n if (id === VIRTUAL_CUSTOM_VIZ) {\n return [\n \"const ct = window.__METABASE_VIZ_API__.columnTypes;\",\n \"export const { isDate, isNumeric, isInteger, isBoolean, isString, isStringLike, isSummable, isNumericBaseType, isDateWithoutTime, isNumber, isFloat, isTime, isFK, isPK, isEntityName, isTitle, isProduct, isSource, isAddress, isScore, isQuantity, isCategory, isAny, isState, isCountry, isCoordinate, isLatitude, isLongitude, isCurrency, isPercentage, isID, isURL, isEmail, isAvatarURL, isImageURL, hasLatitudeAndLongitudeColumns } = ct;\",\n \"export const formatValue = window.__METABASE_VIZ_API__.formatValue;\",\n ].join(\"\\n\");\n }\n return null;\n },\n };\n}\n\nconst NOTIFY_PORT = 5175;\n\n/**\n * Vite plugin that starts a tiny SSE server and sends a \"reload\" event\n * to all connected clients after each rebuild completes.\n * Metabase's frontend connects to this to live-reload the custom viz.\n */\nfunction metabaseNotifyReload() {\n const clients = new Set<import(\"http\").ServerResponse>();\n let server: ReturnType<typeof createServer> | null = null;\n\n return {\n name: \"metabase-notify-reload\",\n\n buildStart() {\n if (server) {\n return;\n }\n server = createServer((req, res) => {\n res.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"Access-Control-Allow-Origin\": \"*\",\n });\n clients.add(res);\n req.on(\"close\", () => clients.delete(res));\n });\n server.listen(NOTIFY_PORT, () => {\n console.log(\n `[metabase-notify] SSE server listening on http://localhost:${NOTIFY_PORT}`,\n );\n });\n\n // Watch public/assets/ for changes — copy to dist/assets/ and notify\n const assetsDir = resolve(__dirname, \"public/assets\");\n watch(assetsDir, { recursive: true }, (_event, filename) => {\n if (!filename) {\n return;\n }\n cpSync(resolve(assetsDir, filename), resolve(__dirname, \"dist/assets\", filename));\n for (const client of clients) {\n client.write(\"data: reload\\n\\n\");\n }\n console.log(`[metabase-notify] Asset changed: ${filename}, notified ${clients.size} client(s)`);\n });\n },\n\n closeBundle() {\n for (const client of clients) {\n client.write(\"data: reload\\n\\n\");\n }\n console.log(\n `[metabase-notify] Build complete, notified ${clients.size} client(s)`,\n );\n },\n };\n}\n\nconst isWatch = process.argv.includes(\"--watch\");\n\nexport default defineConfig({\n plugins: [metabaseVizExternals(), ...(isWatch ? [metabaseNotifyReload()] : [])],\n publicDir: \"public\",\n build: {\n outDir: \"dist\",\n lib: {\n entry: resolve(__dirname, \"src/index.tsx\"),\n formats: [\"iife\"],\n fileName: () => \"index.js\",\n name: \"__customVizPlugin__\",\n },\n },\n preview: {\n port: 5174,\n host: true,\n cors: true,\n },\n});\n", g = "__CUSTOM_VIZ_NAME__", _ = "__CUSTOM_VIZ_VERSION__";
9
+ function v(e, t) {
10
+ return e.split(g).join(t);
11
+ }
12
+ function y(e) {
13
+ return v(p, e).split(_).join(c);
14
+ }
15
+ function b() {
16
+ return h;
17
+ }
18
+ function x() {
19
+ return m;
16
20
  }
17
- function m() {
18
- return c;
21
+ function S(e) {
22
+ return v(d, e);
19
23
  }
20
- function h(e) {
21
- return d(o, e);
24
+ function C(e) {
25
+ return v(f, e);
22
26
  }
23
- function g() {
24
- return a;
27
+ function w() {
28
+ return u;
29
+ }
30
+ var T = a(i(s(import.meta.url)), "templates");
31
+ function E(e) {
32
+ return t(a(T, e));
33
+ }
34
+ function D() {
35
+ return l;
25
36
  }
26
37
  //#endregion
27
38
  //#region src/cli.ts
28
- var _ = new i();
29
- _.name("metabase-custom-viz").description("CLI for creating custom visualizations for Metabase").version("0.0.1"), _.command("init").description("Scaffold a new custom visualization").argument("<name>", "Name of the custom visualization").action(async (i) => {
30
- let a = i.trim().replace(/\s+/g, "-").toLowerCase();
31
- (!a || !/^[a-z0-9@][a-z0-9._\-/]*$/.test(a)) && (console.error(`Error: "${i}" is not a valid project name. Use letters, numbers, hyphens, and dots.`), process.exit(1)), e(a) && (console.error(`Error: Directory "${a}" already exists.`), process.exit(1)), console.log(`Scaffolding custom visualization: ${a}\n`), await t(r(a, "src"), { recursive: !0 }), await Promise.all([
32
- n(r(a, "package.json"), f(a)),
33
- n(r(a, "vite.config.ts"), p()),
34
- n(r(a, "tsconfig.json"), m()),
35
- n(r(a, "src", "index.tsx"), h(a)),
36
- n(r(a, ".gitignore"), g())
37
- ]), console.log("Created files:"), console.log(` ${a}/package.json`), console.log(` ${a}/vite.config.ts`), console.log(` ${a}/tsconfig.json`), console.log(` ${a}/src/index.tsx`), console.log(` ${a}/.gitignore`), console.log(), console.log("Next steps:"), console.log(` cd ${a}`), console.log(" npm install"), console.log(" npm run dev # Watch mode"), console.log(" npm run build # Production build");
38
- }), _.parse();
39
+ var O = new o();
40
+ O.name("metabase-custom-viz").description("CLI for creating custom visualizations for Metabase").version("0.0.1"), O.command("init").description("Scaffold a new custom visualization").argument("<name>", "Name of the custom visualization").action(async (t) => {
41
+ let i = t.trim().replace(/\s+/g, "-").toLowerCase();
42
+ (!i || !/^[a-z0-9@][a-z0-9._\-/]*$/.test(i)) && (console.error(`Error: "${t}" is not a valid project name. Use letters, numbers, hyphens, and dots.`), process.exit(1)), e(i) && (console.error(`Error: Directory "${i}" already exists.`), process.exit(1)), console.log(`Scaffolding custom visualization: ${i}\n`), await Promise.all([n(a(i, "src"), { recursive: !0 }), n(a(i, "public", "assets"), { recursive: !0 })]), await Promise.all([
43
+ r(a(i, "package.json"), y(i)),
44
+ r(a(i, "vite.config.ts"), b()),
45
+ r(a(i, "tsconfig.json"), x()),
46
+ r(a(i, "src", "index.tsx"), S(i)),
47
+ r(a(i, "metabase-plugin.json"), C(i)),
48
+ r(a(i, "public", "assets", "icon.svg"), w()),
49
+ r(a(i, "public", "assets", "thumbs-up.png"), E("thumbs-up.png")),
50
+ r(a(i, "public", "assets", "thumbs-down.png"), E("thumbs-down.png")),
51
+ r(a(i, ".gitignore"), D())
52
+ ]), console.log("Created files:"), console.log(` ${i}/package.json`), console.log(` ${i}/vite.config.ts`), console.log(` ${i}/tsconfig.json`), console.log(` ${i}/src/index.tsx`), console.log(` ${i}/metabase-plugin.json`), console.log(` ${i}/public/assets/icon.svg`), console.log(` ${i}/public/assets/thumbs-up.png`), console.log(` ${i}/public/assets/thumbs-down.png`), console.log(` ${i}/.gitignore`), console.log(), console.log("Next steps:"), console.log(` cd ${i}`), console.log(" npm install"), console.log(" npm run dev # Watch mode"), console.log(" npm run build # Production build");
53
+ }), O.parse();
39
54
  //#endregion
@@ -0,0 +1,40 @@
1
+ import { ColumnPredicate, ColumnTypes } from './types/column-types';
2
+ import { Column } from './types/data';
3
+ export declare const isDate: ColumnPredicate;
4
+ export declare const isNumeric: ColumnPredicate;
5
+ export declare const isInteger: ColumnPredicate;
6
+ export declare const isBoolean: ColumnPredicate;
7
+ export declare const isString: ColumnPredicate;
8
+ export declare const isStringLike: ColumnPredicate;
9
+ export declare const isSummable: ColumnPredicate;
10
+ export declare const isNumericBaseType: ColumnPredicate;
11
+ export declare const isDateWithoutTime: ColumnPredicate;
12
+ export declare const isNumber: ColumnPredicate;
13
+ export declare const isFloat: ColumnPredicate;
14
+ export declare const isTime: ColumnPredicate;
15
+ export declare const isFK: ColumnPredicate;
16
+ export declare const isPK: ColumnPredicate;
17
+ export declare const isEntityName: ColumnPredicate;
18
+ export declare const isTitle: ColumnPredicate;
19
+ export declare const isProduct: ColumnPredicate;
20
+ export declare const isSource: ColumnPredicate;
21
+ export declare const isAddress: ColumnPredicate;
22
+ export declare const isScore: ColumnPredicate;
23
+ export declare const isQuantity: ColumnPredicate;
24
+ export declare const isCategory: ColumnPredicate;
25
+ export declare const isAny: ColumnPredicate;
26
+ export declare const isState: ColumnPredicate;
27
+ export declare const isCountry: ColumnPredicate;
28
+ export declare const isCoordinate: ColumnPredicate;
29
+ export declare const isLatitude: ColumnPredicate;
30
+ export declare const isLongitude: ColumnPredicate;
31
+ export declare const isCurrency: ColumnPredicate;
32
+ export declare const isPercentage: ColumnPredicate;
33
+ export declare const isID: ColumnPredicate;
34
+ export declare const isURL: ColumnPredicate;
35
+ export declare const isEmail: ColumnPredicate;
36
+ export declare const isAvatarURL: ColumnPredicate;
37
+ export declare const isImageURL: ColumnPredicate;
38
+ export declare const hasLatitudeAndLongitudeColumns: (cols: Column[]) => boolean;
39
+ export type { ColumnPredicate, ColumnTypes };
40
+ //# sourceMappingURL=column-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"column-types.d.ts","sourceRoot":"","sources":["../src/column-types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAI3C,eAAO,MAAM,MAAM,iBAAO,CAAC;AAC3B,eAAO,MAAM,SAAS,iBAAO,CAAC;AAC9B,eAAO,MAAM,SAAS,iBAAO,CAAC;AAC9B,eAAO,MAAM,SAAS,iBAAO,CAAC;AAC9B,eAAO,MAAM,QAAQ,iBAAO,CAAC;AAC7B,eAAO,MAAM,YAAY,iBAAO,CAAC;AACjC,eAAO,MAAM,UAAU,iBAAO,CAAC;AAC/B,eAAO,MAAM,iBAAiB,iBAAO,CAAC;AACtC,eAAO,MAAM,iBAAiB,iBAAO,CAAC;AACtC,eAAO,MAAM,QAAQ,iBAAO,CAAC;AAC7B,eAAO,MAAM,OAAO,iBAAO,CAAC;AAC5B,eAAO,MAAM,MAAM,iBAAO,CAAC;AAC3B,eAAO,MAAM,IAAI,iBAAO,CAAC;AACzB,eAAO,MAAM,IAAI,iBAAO,CAAC;AACzB,eAAO,MAAM,YAAY,iBAAO,CAAC;AACjC,eAAO,MAAM,OAAO,iBAAO,CAAC;AAC5B,eAAO,MAAM,SAAS,iBAAO,CAAC;AAC9B,eAAO,MAAM,QAAQ,iBAAO,CAAC;AAC7B,eAAO,MAAM,SAAS,iBAAO,CAAC;AAC9B,eAAO,MAAM,OAAO,iBAAO,CAAC;AAC5B,eAAO,MAAM,UAAU,iBAAO,CAAC;AAC/B,eAAO,MAAM,UAAU,iBAAO,CAAC;AAC/B,eAAO,MAAM,KAAK,iBAAO,CAAC;AAC1B,eAAO,MAAM,OAAO,iBAAO,CAAC;AAC5B,eAAO,MAAM,SAAS,iBAAO,CAAC;AAC9B,eAAO,MAAM,YAAY,iBAAO,CAAC;AACjC,eAAO,MAAM,UAAU,iBAAO,CAAC;AAC/B,eAAO,MAAM,WAAW,iBAAO,CAAC;AAChC,eAAO,MAAM,UAAU,iBAAO,CAAC;AAC/B,eAAO,MAAM,YAAY,iBAAO,CAAC;AACjC,eAAO,MAAM,IAAI,iBAAO,CAAC;AACzB,eAAO,MAAM,KAAK,iBAAO,CAAC;AAC1B,eAAO,MAAM,OAAO,iBAAO,CAAC;AAC5B,eAAO,MAAM,WAAW,iBAAO,CAAC;AAChC,eAAO,MAAM,UAAU,iBAAO,CAAC;AAC/B,eAAO,MAAM,8BAA8B,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OACpD,CAAC;AAEd,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { FormatValue, FormatValueOptions } from './types/format';
2
+ export declare const formatValue: FormatValue;
3
+ export type { FormatValue, FormatValueOptions };
4
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEtE,eAAO,MAAM,WAAW,EAAE,WAAsB,CAAC;AAEjD,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,4 @@
1
1
  export * from './types';
2
+ export * from './column-types';
3
+ export * from './format';
2
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,UAAU,CAAC"}
package/dist/index.js CHANGED
@@ -16,6 +16,6 @@ var e = [
16
16
  "week-of-year",
17
17
  "month-of-year",
18
18
  "quarter-of-year"
19
- ], n = [...e, ...t];
19
+ ], n = [...e, ...t], r = () => !1, i = r, a = r, o = r, s = r, c = r, l = r, u = r, d = r, f = r, p = r, m = r, h = r, g = r, _ = r, v = r, y = r, b = r, x = r, S = r, C = r, w = r, T = r, E = r, D = r, O = r, k = r, A = r, j = r, M = r, N = r, P = r, F = r, I = r, L = r, R = r, z = () => !1, B = () => "";
20
20
  //#endregion
21
- export { e as dateTimeAbsoluteUnits, t as dateTimeRelativeUnits, n as dateTimeUnits };
21
+ export { e as dateTimeAbsoluteUnits, t as dateTimeRelativeUnits, n as dateTimeUnits, B as formatValue, z as hasLatitudeAndLongitudeColumns, S as isAddress, E as isAny, L as isAvatarURL, s as isBoolean, T as isCategory, k as isCoordinate, O as isCountry, M as isCurrency, i as isDate, f as isDateWithoutTime, I as isEmail, v as isEntityName, g as isFK, m as isFloat, P as isID, R as isImageURL, o as isInteger, A as isLatitude, j as isLongitude, p as isNumber, a as isNumeric, d as isNumericBaseType, _ as isPK, N as isPercentage, b as isProduct, w as isQuantity, C as isScore, x as isSource, D as isState, c as isString, l as isStringLike, u as isSummable, h as isTime, y as isTitle, F as isURL };
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2
+ <rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
3
+ <line x1="3" y1="9" x2="21" y2="9"/>
4
+ <line x1="9" y1="21" x2="9" y2="9"/>
5
+ </svg>
@@ -0,0 +1,136 @@
1
+ import type {
2
+ CreateCustomVisualization,
3
+ CustomStaticVisualizationProps,
4
+ CustomVisualizationProps,
5
+ } from "@metabase/custom-viz";
6
+
7
+ type Settings = {
8
+ threshold?: number;
9
+ };
10
+
11
+ const createVisualization: CreateCustomVisualization<Settings> = ({
12
+ getAssetUrl,
13
+ }) => {
14
+ return {
15
+ id: "__CUSTOM_VIZ_NAME__",
16
+ getName: () => "__CUSTOM_VIZ_NAME__",
17
+ minSize: { width: 1, height: 1 },
18
+ defaultSize: { width: 2, height: 2 },
19
+ isSensible({ cols, rows }) {
20
+ return cols.length === 1 && rows.length === 1 && typeof rows[0][0] === "number";
21
+ },
22
+ checkRenderable(series, settings) {
23
+ if (series.length !== 1) {
24
+ throw new Error("Only 1 series is supported");
25
+ }
26
+
27
+ const [
28
+ {
29
+ data: { cols, rows },
30
+ },
31
+ ] = series;
32
+
33
+ if (cols.length !== 1) {
34
+ throw new Error("Query results should only have 1 column");
35
+ }
36
+
37
+ if (rows.length !== 1) {
38
+ throw new Error("Query results should only have 1 row");
39
+ }
40
+
41
+ if (typeof rows[0][0] !== "number") {
42
+ throw new Error("Result is not a number");
43
+ }
44
+
45
+ if (typeof settings.threshold !== "number") {
46
+ throw new Error("Threshold setting is not set");
47
+ }
48
+ },
49
+ settings: {
50
+ threshold: {
51
+ id: "1",
52
+ title: "Threshold",
53
+ widget: "number",
54
+ getDefault() {
55
+ return 0;
56
+ },
57
+ getProps() {
58
+ return {
59
+ options: {
60
+ isInteger: false,
61
+ isNonNegative: false,
62
+ },
63
+ placeholder: "Set threshold",
64
+ };
65
+ },
66
+ },
67
+ },
68
+ VisualizationComponent: makeVisualizationComponent(getAssetUrl),
69
+ StaticVisualizationComponent: makeStaticVisualizationComponent(getAssetUrl),
70
+ };
71
+ };
72
+
73
+ const makeVisualizationComponent = (getAssetUrl: (path: string) => string) => (props: CustomVisualizationProps<Settings>) => {
74
+ const { height, series, settings, width } = props;
75
+ const { threshold } = settings;
76
+ const value = series[0].data.rows[0][0];
77
+
78
+ if (typeof value !== "number" || typeof threshold !== "number") {
79
+ throw new Error("Value and threshold need to be numbers");
80
+ }
81
+
82
+ const emoji = value >= threshold ? "👍" : "👎";
83
+
84
+ return (
85
+ <div
86
+ style={{
87
+ display: "flex",
88
+ justifyContent: "center",
89
+ alignItems: "center",
90
+ width,
91
+ height,
92
+ fontSize: "10rem",
93
+ }}
94
+ >
95
+ {emoji}
96
+ </div>
97
+ );
98
+ };
99
+
100
+ const makeStaticVisualizationComponent = (getAssetUrl: (path: string) => string) => (
101
+ props: CustomStaticVisualizationProps<Settings>,
102
+ ) => {
103
+ const width = 540;
104
+ const height = 360;
105
+ const { series, settings } = props;
106
+ const { threshold } = settings;
107
+ const value = series[0].data.rows[0][0];
108
+
109
+ if (typeof value !== "number" || typeof threshold !== "number") {
110
+ throw new Error("Value and threshold need to be numbers");
111
+ }
112
+
113
+ const emoji =
114
+ value >= threshold ? (
115
+ <img src={getAssetUrl("thumbs-up.png")} />
116
+ ) : (
117
+ <img src={getAssetUrl("thumbs-down.png")} />
118
+ );
119
+
120
+ return (
121
+ <div
122
+ style={{
123
+ display: "flex",
124
+ justifyContent: "center",
125
+ alignItems: "center",
126
+ width,
127
+ height,
128
+ fontSize: "10rem",
129
+ }}
130
+ >
131
+ {emoji}
132
+ </div>
133
+ );
134
+ };
135
+
136
+ export default createVisualization;
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "__CUSTOM_VIZ_NAME__",
3
+ "icon": "icon.svg",
4
+ "assets": ["icon.svg", "thumbs-up.png", "thumbs-down.png"],
5
+ "metabase": {
6
+ "version": ">=59"
7
+ }
8
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "__CUSTOM_VIZ_NAME__",
3
+ "version": "0.0.1",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "build": "vite build",
8
+ "dev": "vite build --watch & vite preview",
9
+ "type-check": "tsc --noEmit"
10
+ },
11
+ "devDependencies": {
12
+ "@metabase/custom-viz": "__CUSTOM_VIZ_VERSION__",
13
+ "@types/react": "^19.2.14",
14
+ "react": "^19.1.0",
15
+ "typescript": "^5.9.3",
16
+ "vite": "^8.0.0"
17
+ }
18
+ }
Binary file
Binary file
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "jsx": "react-jsx",
7
+ "strict": true,
8
+ "esModuleInterop": true,
9
+ "skipLibCheck": true,
10
+ "outDir": "dist",
11
+ "declaration": true
12
+ },
13
+ "include": ["src"]
14
+ }
@@ -0,0 +1,139 @@
1
+ import { resolve } from "path";
2
+ import { createServer } from "http";
3
+ import { watch, cpSync } from "fs";
4
+ import { defineConfig } from "vite";
5
+
6
+ /**
7
+ * Vite plugin that replaces `react` and `react/jsx-runtime` imports with
8
+ * virtual modules that read from Metabase's `window.__METABASE_VIZ_API__`.
9
+ *
10
+ * This is necessary because the ES module output format cannot use
11
+ * `output.globals`, and bare `import 'react'` would fail in the browser.
12
+ */
13
+ function metabaseVizExternals() {
14
+ const VIRTUAL_REACT = "\0virtual:react";
15
+ const VIRTUAL_JSX_RUNTIME = "\0virtual:react/jsx-runtime";
16
+ const VIRTUAL_CUSTOM_VIZ = "\0virtual:@metabase/custom-viz";
17
+
18
+ return {
19
+ name: "metabase-viz-externals",
20
+ enforce: "pre" as const,
21
+
22
+ resolveId(source) {
23
+ if (source === "react") {
24
+ return VIRTUAL_REACT;
25
+ }
26
+ if (source === "react/jsx-runtime") {
27
+ return VIRTUAL_JSX_RUNTIME;
28
+ }
29
+ if (source === "@metabase/custom-viz") {
30
+ return VIRTUAL_CUSTOM_VIZ;
31
+ }
32
+ return null;
33
+ },
34
+
35
+ load(id) {
36
+ if (id === VIRTUAL_REACT) {
37
+ return [
38
+ "const React = window.__METABASE_VIZ_API__.React;",
39
+ "export default React;",
40
+ "export const { useState, useEffect, useRef, useCallback, useMemo, useReducer, useContext, createElement, Fragment } = React;",
41
+ ].join("\n");
42
+ }
43
+ if (id === VIRTUAL_JSX_RUNTIME) {
44
+ return [
45
+ "const jsxRuntime = window.__METABASE_VIZ_API__.jsxRuntime;",
46
+ "export const { jsx, jsxs, Fragment } = jsxRuntime;",
47
+ ].join("\n");
48
+ }
49
+ if (id === VIRTUAL_CUSTOM_VIZ) {
50
+ return [
51
+ "const ct = window.__METABASE_VIZ_API__.columnTypes;",
52
+ "export const { isDate, isNumeric, isInteger, isBoolean, isString, isStringLike, isSummable, isNumericBaseType, isDateWithoutTime, isNumber, isFloat, isTime, isFK, isPK, isEntityName, isTitle, isProduct, isSource, isAddress, isScore, isQuantity, isCategory, isAny, isState, isCountry, isCoordinate, isLatitude, isLongitude, isCurrency, isPercentage, isID, isURL, isEmail, isAvatarURL, isImageURL, hasLatitudeAndLongitudeColumns } = ct;",
53
+ "export const formatValue = window.__METABASE_VIZ_API__.formatValue;",
54
+ ].join("\n");
55
+ }
56
+ return null;
57
+ },
58
+ };
59
+ }
60
+
61
+ const NOTIFY_PORT = 5175;
62
+
63
+ /**
64
+ * Vite plugin that starts a tiny SSE server and sends a "reload" event
65
+ * to all connected clients after each rebuild completes.
66
+ * Metabase's frontend connects to this to live-reload the custom viz.
67
+ */
68
+ function metabaseNotifyReload() {
69
+ const clients = new Set<import("http").ServerResponse>();
70
+ let server: ReturnType<typeof createServer> | null = null;
71
+
72
+ return {
73
+ name: "metabase-notify-reload",
74
+
75
+ buildStart() {
76
+ if (server) {
77
+ return;
78
+ }
79
+ server = createServer((req, res) => {
80
+ res.writeHead(200, {
81
+ "Content-Type": "text/event-stream",
82
+ "Cache-Control": "no-cache",
83
+ Connection: "keep-alive",
84
+ "Access-Control-Allow-Origin": "*",
85
+ });
86
+ clients.add(res);
87
+ req.on("close", () => clients.delete(res));
88
+ });
89
+ server.listen(NOTIFY_PORT, () => {
90
+ console.log(
91
+ `[metabase-notify] SSE server listening on http://localhost:${NOTIFY_PORT}`,
92
+ );
93
+ });
94
+
95
+ // Watch public/assets/ for changes — copy to dist/assets/ and notify
96
+ const assetsDir = resolve(__dirname, "public/assets");
97
+ watch(assetsDir, { recursive: true }, (_event, filename) => {
98
+ if (!filename) {
99
+ return;
100
+ }
101
+ cpSync(resolve(assetsDir, filename), resolve(__dirname, "dist/assets", filename));
102
+ for (const client of clients) {
103
+ client.write("data: reload\n\n");
104
+ }
105
+ console.log(`[metabase-notify] Asset changed: ${filename}, notified ${clients.size} client(s)`);
106
+ });
107
+ },
108
+
109
+ closeBundle() {
110
+ for (const client of clients) {
111
+ client.write("data: reload\n\n");
112
+ }
113
+ console.log(
114
+ `[metabase-notify] Build complete, notified ${clients.size} client(s)`,
115
+ );
116
+ },
117
+ };
118
+ }
119
+
120
+ const isWatch = process.argv.includes("--watch");
121
+
122
+ export default defineConfig({
123
+ plugins: [metabaseVizExternals(), ...(isWatch ? [metabaseNotifyReload()] : [])],
124
+ publicDir: "public",
125
+ build: {
126
+ outDir: "dist",
127
+ lib: {
128
+ entry: resolve(__dirname, "src/index.tsx"),
129
+ formats: ["iife"],
130
+ fileName: () => "index.js",
131
+ name: "__customVizPlugin__",
132
+ },
133
+ },
134
+ preview: {
135
+ port: 5174,
136
+ host: true,
137
+ cors: true,
138
+ },
139
+ });
@@ -0,0 +1,41 @@
1
+ import { Column } from './data';
2
+ export type ColumnPredicate = (column: Column | null | undefined) => boolean;
3
+ export interface ColumnTypes {
4
+ isDate: ColumnPredicate;
5
+ isNumeric: ColumnPredicate;
6
+ isInteger: ColumnPredicate;
7
+ isBoolean: ColumnPredicate;
8
+ isString: ColumnPredicate;
9
+ isStringLike: ColumnPredicate;
10
+ isSummable: ColumnPredicate;
11
+ isNumericBaseType: ColumnPredicate;
12
+ isDateWithoutTime: ColumnPredicate;
13
+ isNumber: ColumnPredicate;
14
+ isFloat: ColumnPredicate;
15
+ isTime: ColumnPredicate;
16
+ isFK: ColumnPredicate;
17
+ isPK: ColumnPredicate;
18
+ isEntityName: ColumnPredicate;
19
+ isTitle: ColumnPredicate;
20
+ isProduct: ColumnPredicate;
21
+ isSource: ColumnPredicate;
22
+ isAddress: ColumnPredicate;
23
+ isScore: ColumnPredicate;
24
+ isQuantity: ColumnPredicate;
25
+ isCategory: ColumnPredicate;
26
+ isAny: ColumnPredicate;
27
+ isState: ColumnPredicate;
28
+ isCountry: ColumnPredicate;
29
+ isCoordinate: ColumnPredicate;
30
+ isLatitude: ColumnPredicate;
31
+ isLongitude: ColumnPredicate;
32
+ isCurrency: ColumnPredicate;
33
+ isPercentage: ColumnPredicate;
34
+ isID: ColumnPredicate;
35
+ isURL: ColumnPredicate;
36
+ isEmail: ColumnPredicate;
37
+ isAvatarURL: ColumnPredicate;
38
+ isImageURL: ColumnPredicate;
39
+ hasLatitudeAndLongitudeColumns: (cols: Column[]) => boolean;
40
+ }
41
+ //# sourceMappingURL=column-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"column-types.d.ts","sourceRoot":"","sources":["../../src/types/column-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC,MAAM,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,KAAK,OAAO,CAAC;AAE7E,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,eAAe,CAAC;IACxB,SAAS,EAAE,eAAe,CAAC;IAC3B,SAAS,EAAE,eAAe,CAAC;IAC3B,SAAS,EAAE,eAAe,CAAC;IAC3B,QAAQ,EAAE,eAAe,CAAC;IAC1B,YAAY,EAAE,eAAe,CAAC;IAC9B,UAAU,EAAE,eAAe,CAAC;IAC5B,iBAAiB,EAAE,eAAe,CAAC;IACnC,iBAAiB,EAAE,eAAe,CAAC;IACnC,QAAQ,EAAE,eAAe,CAAC;IAC1B,OAAO,EAAE,eAAe,CAAC;IACzB,MAAM,EAAE,eAAe,CAAC;IACxB,IAAI,EAAE,eAAe,CAAC;IACtB,IAAI,EAAE,eAAe,CAAC;IACtB,YAAY,EAAE,eAAe,CAAC;IAC9B,OAAO,EAAE,eAAe,CAAC;IACzB,SAAS,EAAE,eAAe,CAAC;IAC3B,QAAQ,EAAE,eAAe,CAAC;IAC1B,SAAS,EAAE,eAAe,CAAC;IAC3B,OAAO,EAAE,eAAe,CAAC;IACzB,UAAU,EAAE,eAAe,CAAC;IAC5B,UAAU,EAAE,eAAe,CAAC;IAC5B,KAAK,EAAE,eAAe,CAAC;IACvB,OAAO,EAAE,eAAe,CAAC;IACzB,SAAS,EAAE,eAAe,CAAC;IAC3B,YAAY,EAAE,eAAe,CAAC;IAC9B,UAAU,EAAE,eAAe,CAAC;IAC5B,WAAW,EAAE,eAAe,CAAC;IAC7B,UAAU,EAAE,eAAe,CAAC;IAC5B,YAAY,EAAE,eAAe,CAAC;IAC9B,IAAI,EAAE,eAAe,CAAC;IACtB,KAAK,EAAE,eAAe,CAAC;IACvB,OAAO,EAAE,eAAe,CAAC;IACzB,WAAW,EAAE,eAAe,CAAC;IAC7B,UAAU,EAAE,eAAe,CAAC;IAC5B,8BAA8B,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC;CAC7D"}
@@ -0,0 +1,22 @@
1
+ import { Column, RowValue } from './data';
2
+ export type FormatValueOptions = {
3
+ column?: Column;
4
+ compact?: boolean;
5
+ currency?: string;
6
+ currency_style?: string;
7
+ date_format?: string;
8
+ date_style?: string;
9
+ date_separator?: string;
10
+ date_abbreviate?: boolean;
11
+ decimals?: number;
12
+ number_style?: string;
13
+ number_separators?: string;
14
+ prefix?: string;
15
+ suffix?: string;
16
+ scale?: number;
17
+ negativeInParentheses?: boolean;
18
+ time_enabled?: "minutes" | "milliseconds" | "seconds" | null;
19
+ time_style?: string;
20
+ };
21
+ export type FormatValue = (value: RowValue, options?: FormatValueOptions) => string;
22
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/types/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAE/C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,YAAY,CAAC,EAAE,SAAS,GAAG,cAAc,GAAG,SAAS,GAAG,IAAI,CAAC;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,CACxB,KAAK,EAAE,QAAQ,EACf,OAAO,CAAC,EAAE,kBAAkB,KACzB,MAAM,CAAC"}
@@ -1,5 +1,8 @@
1
+ export * from './column-types';
1
2
  export * from './data';
2
3
  export * from './date-time';
4
+ export * from './format';
5
+ export * from './measure-text';
3
6
  export * from './viz';
4
7
  export * from './viz-settings';
5
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,aAAa,CAAC;AAC5B,cAAc,OAAO,CAAC;AACtB,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,QAAQ,CAAC;AACvB,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,OAAO,CAAC;AACtB,cAAc,gBAAgB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { ComponentType } from 'react';
2
2
  import { Column, DatasetData, RowValue, Series } from './data';
3
- import { TextHeightMeasurer, TextWidthMeasurer } from './measure-text';
3
+ import { TextHeightMeasurer, TextMeasurer, TextWidthMeasurer } from './measure-text';
4
4
  import { WidgetName, Widgets } from './viz-settings';
5
5
  /**
6
6
  * Export this function to define a custom visualization.
@@ -11,6 +11,24 @@ export type CreateCustomVisualizationProps = {
11
11
  * Translates text using ttag function used in Metabase.
12
12
  */
13
13
  translate: (text: string) => string;
14
+ /**
15
+ * Returns a URL for a static asset declared in the plugin manifest.
16
+ * Use this to reference images and other static files from your plugin.
17
+ * @example getAssetUrl("icon.svg")
18
+ */
19
+ getAssetUrl: (assetPath: string) => string;
20
+ /**
21
+ * Measures text dimensions (width and height) for given text and font style.
22
+ */
23
+ measureText: TextMeasurer;
24
+ /**
25
+ * Measures text width for given text and font style.
26
+ */
27
+ measureTextWidth: TextWidthMeasurer;
28
+ /**
29
+ * Measures text height for given text and font style.
30
+ */
31
+ measureTextHeight: TextHeightMeasurer;
14
32
  };
15
33
  export type CustomVisualization<CustomVisualizationSettings> = {
16
34
  /**
@@ -32,11 +50,11 @@ export type CustomVisualization<CustomVisualizationSettings> = {
32
50
  /**
33
51
  * Min size on dashboard grid.
34
52
  */
35
- minSize: VisualizationGridSize;
53
+ minSize?: VisualizationGridSize;
36
54
  /**
37
55
  * Default size on dashboard grid.
38
56
  */
39
- defaultSize: VisualizationGridSize;
57
+ defaultSize?: VisualizationGridSize;
40
58
  /**
41
59
  * Visualization settings definitions.
42
60
  */
@@ -57,10 +75,10 @@ export type CustomVisualization<CustomVisualizationSettings> = {
57
75
  /**
58
76
  * Component that renders the visualization.
59
77
  */
60
- StaticVisualizationComponent: ComponentType<CustomStaticVisualizationProps<CustomVisualizationSettings>>;
78
+ StaticVisualizationComponent?: ComponentType<CustomStaticVisualizationProps<CustomVisualizationSettings>>;
61
79
  };
62
80
  export type CustomVisualizationSettingsDefinitions<CustomVisualizationSettings, K extends keyof CustomVisualizationSettings = keyof CustomVisualizationSettings> = {
63
- [Key in K]-?: VisualizationSettingDefinition<unknown, CustomVisualizationSettings[Key], Record<string, unknown>, CustomVisualizationSettings>;
81
+ [Key in K]-?: VisualizationSettingDefinition<CustomVisualizationSettings[Key], Record<string, unknown>, CustomVisualizationSettings>;
64
82
  };
65
83
  export type BaseWidgetProps<TValue, CustomVisualizationSettings> = {
66
84
  id: string;
@@ -68,7 +86,7 @@ export type BaseWidgetProps<TValue, CustomVisualizationSettings> = {
68
86
  onChange: (value?: TValue | null) => void;
69
87
  onChangeSettings: (settings: Partial<CustomVisualizationSettings>) => void;
70
88
  };
71
- type VisualizationSettingDefinitionBase<T, TValue, CustomVisualizationSettings> = {
89
+ type VisualizationSettingDefinitionBase<TValue, CustomVisualizationSettings> = {
72
90
  id: string;
73
91
  section?: string;
74
92
  title?: string;
@@ -82,25 +100,25 @@ type VisualizationSettingDefinitionBase<T, TValue, CustomVisualizationSettings>
82
100
  readDependencies?: string[];
83
101
  writeDependencies?: string[];
84
102
  eraseDependencies?: string[];
85
- isValid?: (object: T, settings: CustomVisualizationSettings) => boolean;
86
- getDefault?: (object: T, settings: CustomVisualizationSettings) => TValue;
87
- getValue?: (object: T, settings: CustomVisualizationSettings) => TValue;
103
+ isValid?: (series: Series, settings: CustomVisualizationSettings) => boolean;
104
+ getDefault?: (series: Series, settings: CustomVisualizationSettings) => TValue;
105
+ getValue?: (series: Series, settings: CustomVisualizationSettings) => TValue;
88
106
  };
89
- type VisualizationSettingDefinitionWithBuiltInWidget<T, TValue, CustomVisualizationSettings> = {
90
- [Key in WidgetName]: VisualizationSettingDefinitionBase<T, TValue, CustomVisualizationSettings> & {
107
+ type VisualizationSettingDefinitionWithBuiltInWidget<TValue, CustomVisualizationSettings> = {
108
+ [Key in WidgetName]: VisualizationSettingDefinitionBase<TValue, CustomVisualizationSettings> & {
91
109
  widget: Key;
92
- getProps?: (object: T, vizSettings: CustomVisualizationSettings) => Widgets[Key];
110
+ getProps?: (object: Series, vizSettings: CustomVisualizationSettings) => Widgets[Key];
93
111
  };
94
112
  }[WidgetName];
95
- type VisualizationSettingDefinitionWithCustomWidget<T, TValue, TProps, CustomVisualizationSettings> = VisualizationSettingDefinitionBase<T, TValue, CustomVisualizationSettings> & {
113
+ type VisualizationSettingDefinitionWithCustomWidget<TValue, TProps, CustomVisualizationSettings> = VisualizationSettingDefinitionBase<TValue, CustomVisualizationSettings> & {
96
114
  widget: ComponentType<TProps & BaseWidgetProps<TValue, CustomVisualizationSettings>>;
97
- getProps?: (object: T, vizSettings: CustomVisualizationSettings) => TProps;
115
+ getProps?: (object: Series, vizSettings: CustomVisualizationSettings) => TProps;
98
116
  };
99
- type VisualizationSettingDefinitionWithoutWidget<T, TValue, CustomVisualizationSettings> = VisualizationSettingDefinitionBase<T, TValue, CustomVisualizationSettings> & {
117
+ type VisualizationSettingDefinitionWithoutWidget<TValue, CustomVisualizationSettings> = VisualizationSettingDefinitionBase<TValue, CustomVisualizationSettings> & {
100
118
  widget?: never;
101
119
  getProps?: never;
102
120
  };
103
- export type VisualizationSettingDefinition<T, TValue, TProps, CustomVisualizationSettings> = VisualizationSettingDefinitionWithBuiltInWidget<T, TValue, CustomVisualizationSettings> | VisualizationSettingDefinitionWithCustomWidget<T, TValue, TProps, CustomVisualizationSettings> | VisualizationSettingDefinitionWithoutWidget<T, TValue, CustomVisualizationSettings>;
121
+ export type VisualizationSettingDefinition<TValue, TProps, CustomVisualizationSettings> = VisualizationSettingDefinitionWithBuiltInWidget<TValue, CustomVisualizationSettings> | VisualizationSettingDefinitionWithCustomWidget<TValue, TProps, CustomVisualizationSettings> | VisualizationSettingDefinitionWithoutWidget<TValue, CustomVisualizationSettings>;
104
122
  export type VisualizationGridSize = {
105
123
  /**
106
124
  * Number of grid columns in a Metabase dashboard.
@@ -1 +1 @@
1
- {"version":3,"file":"viz.d.ts","sourceRoot":"","sources":["../../src/types/viz.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"viz.d.ts","sourceRoot":"","sources":["../../src/types/viz.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metabase/custom-viz",
3
- "version": "0.0.1-alpha.1",
3
+ "version": "0.0.1-alpha.3",
4
4
  "description": "Creating custom visualizations for Metabase",
5
5
  "type": "module",
6
6
  "bin": {
@@ -22,11 +22,11 @@
22
22
  ],
23
23
  "scripts": {
24
24
  "dev": "vite build --watch",
25
- "build": "vite build",
25
+ "build": "vite build && cp -r src/templates dist/templates",
26
26
  "lint": "oxlint src/",
27
27
  "format": "prettier --write .",
28
28
  "format:check": "prettier --check .",
29
- "release": "npm run build && np"
29
+ "release": "bun run build && np"
30
30
  },
31
31
  "dependencies": {
32
32
  "commander": "^13.1.0"
@@ -34,7 +34,6 @@
34
34
  "devDependencies": {
35
35
  "@types/node": "^22.13.10",
36
36
  "@types/react": "^19.2.14",
37
- "lefthook": "^2.1.4",
38
37
  "np": "^10.2.0",
39
38
  "prettier": "^3.5.3",
40
39
  "typescript": "^5.9.3",