diff --git a/package-lock.json b/package-lock.json index 6151e571..54082abc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3317,6 +3317,10 @@ "langium": "3.3.1" } }, + "node_modules/@microsoft/chartifact-chrome-extension": { + "resolved": "packages/chrome-extension", + "link": true + }, "node_modules/@microsoft/chartifact-compiler": { "resolved": "packages/compiler", "link": true @@ -4235,6 +4239,17 @@ "@babel/types": "^7.28.2" } }, + "node_modules/@types/chrome": { + "version": "0.0.280", + "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.280.tgz", + "integrity": "sha512-AotSmZrL9bcZDDmSI1D9dE7PGbhOur5L0cKxXd7IqbVizQWCY4gcvupPUVsQ4FfDj3V2tt/iOpomT9EY0s+w1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/filesystem": "*", + "@types/har-format": "*" + } + }, "node_modules/@types/css-tree": { "version": "2.3.10", "resolved": "https://registry.npmjs.org/@types/css-tree/-/css-tree-2.3.10.tgz", @@ -4532,12 +4547,36 @@ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "license": "MIT" }, + "node_modules/@types/filesystem": { + "version": "0.0.36", + "resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.36.tgz", + "integrity": "sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/filewriter": "*" + } + }, + "node_modules/@types/filewriter": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.33.tgz", + "integrity": "sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/geojson": { "version": "7946.0.16", "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", "license": "MIT" }, + "node_modules/@types/har-format": { + "version": "1.2.16", + "resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.16.tgz", + "integrity": "sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/js-yaml": { "version": "4.0.9", "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", @@ -15307,6 +15346,525 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "packages/chrome-extension": { + "name": "@microsoft/chartifact-chrome-extension", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "@microsoft/chartifact-compiler": "1.0.0", + "@microsoft/chartifact-sandbox": "1.0.0", + "@microsoft/chartifact-schema": "1.0.0" + }, + "devDependencies": { + "@types/chrome": "^0.0.280", + "vite": "^5.0.0" + } + }, + "packages/chrome-extension/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "packages/chrome-extension/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "packages/chrome-extension/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "packages/chrome-extension/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "packages/chrome-extension/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "packages/chrome-extension/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "packages/chrome-extension/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "packages/chrome-extension/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "packages/chrome-extension/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "packages/chrome-extension/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "packages/chrome-extension/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "packages/chrome-extension/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "packages/chrome-extension/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "packages/chrome-extension/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "packages/chrome-extension/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "packages/chrome-extension/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "packages/chrome-extension/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "packages/chrome-extension/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "packages/chrome-extension/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "packages/chrome-extension/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "packages/chrome-extension/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "packages/chrome-extension/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "packages/chrome-extension/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "packages/chrome-extension/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "packages/chrome-extension/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "packages/chrome-extension/node_modules/vite": { + "version": "5.4.19", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", + "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, "packages/common": { "version": "1.0.0", "license": "MIT" @@ -15385,7 +15943,7 @@ }, "packages/vscode": { "name": "chartifact", - "version": "1.0.0", + "version": "1.0.2", "license": "MIT", "devDependencies": { "@vscode/vsce": "^3.6.0" diff --git a/packages/chrome-extension/.gitignore b/packages/chrome-extension/.gitignore new file mode 100644 index 00000000..2438fdad --- /dev/null +++ b/packages/chrome-extension/.gitignore @@ -0,0 +1,4 @@ +dist/ +*.zip +node_modules/ +.DS_Store \ No newline at end of file diff --git a/packages/chrome-extension/INSTALL.md b/packages/chrome-extension/INSTALL.md new file mode 100644 index 00000000..fff080eb --- /dev/null +++ b/packages/chrome-extension/INSTALL.md @@ -0,0 +1,108 @@ +# Installing the Chartifact Chrome Extension + +This guide helps you install and test the Chartifact Chrome extension for viewing Interactive Document files. + +## Quick Start + +### 1. Build the Extension + +From the repository root: + +```bash +# Install dependencies (if not already done) +npm install + +# Build the extension +npm run build --workspace=@microsoft/chartifact-chrome-extension +``` + +### 2. Load in Chrome + +1. Open Chrome and navigate to `chrome://extensions/` +2. Enable **Developer mode** (toggle in the top-right corner) +3. Click **Load unpacked** +4. Select the folder: `packages/chrome-extension/dist` +5. The extension should now appear in your extensions list + +### 3. Test the Extension + +**Option A: Use the test page** +1. Open `packages/chrome-extension/test.html` in Chrome +2. Click on one of the test file links +3. Look for the "📊 View as Interactive Document" button + +**Option B: Test with local files** +1. Download any `.idoc.json` or `.idoc.md` file +2. Open it in Chrome (drag and drop into browser) +3. The extension should detect it and show the view button + +### 4. Using the Extension + +- **Automatic Detection**: The extension automatically detects `.idoc.md` and `.idoc.json` files +- **View Button**: Look for the floating "📊 View as Interactive Document" button in the top-right +- **Extension Popup**: Click the extension icon to see status and controls +- **Secure Rendering**: All content is rendered in a sandboxed iframe for security + +## Example Files to Test + +You can test with these example files in the repository: +- `packages/examples/json/seattle-weather/6.idoc.json` +- `packages/chrome-extension/test.idoc.json` (simple test file) + +## Troubleshooting + +### Extension not detecting files +- Ensure the file has `.idoc.md` or `.idoc.json` extension +- Check that the extension is enabled in `chrome://extensions/` +- Try refreshing the page + +### View button not appearing +- Make sure you're viewing an actual idoc file (not just a page that links to one) +- Check browser console for any JavaScript errors +- Verify the extension has proper permissions + +### Viewer not loading +- Check browser console for errors +- Ensure the chartifact libraries are properly loaded +- Verify the file content is valid Interactive Document format + +### Permission issues +- The extension needs `activeTab` permission to function +- For local files, you may need to enable "Allow access to file URLs" in the extension details + +## For Developers + +### Building from Source +```bash +cd packages/chrome-extension +npm run build # Build the extension +npm run package # Create distributable ZIP +npm run dev # Watch mode for development +``` + +### Extension Structure +- `src/manifest.json` - Extension manifest +- `src/background.ts` - Service worker +- `src/content.ts` - Content script for detection and injection +- `src/popup.ts/html` - Extension popup interface +- `dist/` - Built extension (load this folder in Chrome) + +### Distribution +- Run `npm run package` to create `chartifact-chrome-extension.zip` +- This ZIP can be uploaded to Chrome Web Store or distributed manually + +## Security + +The extension follows security best practices: +- Minimal permissions (only `activeTab` and `storage`) +- Sandboxed iframe rendering +- No remote code execution +- Content Security Policy compliant + +## Support + +For issues or questions: +1. Check the browser console for error messages +2. Verify file format with the Interactive Document schema +3. Test with the provided example files first +4. Create an issue in the Chartifact repository \ No newline at end of file diff --git a/packages/chrome-extension/README.md b/packages/chrome-extension/README.md new file mode 100644 index 00000000..2c4f3c93 --- /dev/null +++ b/packages/chrome-extension/README.md @@ -0,0 +1,97 @@ +# Chartifact Chrome Extension + +A Chrome extension for viewing Interactive Document files (*.idoc.md and *.idoc.json) with rich visualizations and charts. + +## Features + +- **File Detection**: Automatically detects `.idoc.md` and `.idoc.json` files in web pages +- **Secure Rendering**: Uses sandboxed iframe rendering for security +- **Rich Visualizations**: Supports charts, graphs, and interactive elements +- **Easy Access**: Simple button to open the viewer overlay + +## Installation + +### For Development + +1. Build the extension: + ```bash + cd packages/chrome-extension + npm run build + ``` + +2. Load the extension in Chrome: + - Open Chrome and go to `chrome://extensions/` + - Enable "Developer mode" (toggle in top right) + - Click "Load unpacked" + - Select the `packages/chrome-extension/dist` folder + +### For Distribution + +1. Package the extension: + ```bash + cd packages/chrome-extension + npm run package + ``` + +2. This creates `chartifact-chrome-extension.zip` that can be uploaded to the Chrome Web Store. + +## Usage + +1. Navigate to any `.idoc.md` or `.idoc.json` file in your browser +2. The extension will automatically detect the file and show a "View as Interactive Document" button +3. Click the button to open the Chartifact viewer overlay +4. You can also use the extension popup (click the extension icon) to open the viewer + +## Supported File Types + +- **`.idoc.md`**: Interactive Document markdown files +- **`.idoc.json`**: Interactive Document JSON files + +## Technical Details + +The extension uses: +- **Manifest V3** for modern Chrome extension standards +- **Content Scripts** to detect and inject the viewer +- **Sandbox Rendering** for security using iframes +- **UMD Bundles** for the Chartifact compiler and sandbox libraries + +## Security + +The extension follows security best practices: +- Uses sandboxed iframe rendering to isolate document content +- Minimal permissions (only `activeTab` and `storage`) +- Content Security Policy compliance +- No remote code execution + +## Development + +### Build Process + +1. TypeScript compilation (`npm run tsc`) +2. Vite bundling (`npm run bundle`) +3. Resource copying (`npm run resources`) + +### File Structure + +``` +src/ +├── manifest.json # Extension manifest +├── background.ts # Service worker +├── content.ts # Content script +├── popup.ts # Popup script +└── popup.html # Popup HTML + +dist/ # Built extension +├── manifest.json +├── background.js +├── content.js +├── popup.js +├── popup.html +├── chartifact.sandbox.js +├── chartifact.compiler.js +└── icons/ +``` + +## License + +MIT License - see LICENSE file for details. \ No newline at end of file diff --git a/packages/chrome-extension/package.json b/packages/chrome-extension/package.json new file mode 100644 index 00000000..741a24ab --- /dev/null +++ b/packages/chrome-extension/package.json @@ -0,0 +1,30 @@ +{ + "name": "@microsoft/chartifact-chrome-extension", + "version": "1.0.0", + "description": "Chrome extension for viewing Interactive Document files (*.idoc.md and *.idoc.json)", + "type": "module", + "private": true, + "main": "dist/background.js", + "scripts": { + "clean": "rimraf dist", + "resources": "node scripts/resources.mjs", + "prebuild": "npm run resources", + "tsc": "tsc -p .", + "build": "npm run tsc && npm run bundle", + "bundle": "vite build", + "build:08": "npm run build", + "package": "cd dist && zip -r ../chartifact-chrome-extension.zip .", + "dev": "vite build --watch" + }, + "dependencies": { + "@microsoft/chartifact-sandbox": "1.0.0", + "@microsoft/chartifact-compiler": "1.0.0", + "@microsoft/chartifact-schema": "1.0.0" + }, + "devDependencies": { + "@types/chrome": "^0.0.280", + "vite": "^5.0.0" + }, + "author": "Dan Marshall", + "license": "MIT" +} \ No newline at end of file diff --git a/packages/chrome-extension/scripts/resources.mjs b/packages/chrome-extension/scripts/resources.mjs new file mode 100644 index 00000000..40057776 --- /dev/null +++ b/packages/chrome-extension/scripts/resources.mjs @@ -0,0 +1,84 @@ +/** + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ + +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const distDir = path.join(__dirname, '..', 'dist'); +const srcDir = path.join(__dirname, '..', 'src'); + +// Ensure dist directory exists +if (!fs.existsSync(distDir)) { + fs.mkdirSync(distDir, { recursive: true }); +} + +// Copy static files +const staticFiles = [ + 'manifest.json', + 'popup.html' +]; + +for (const file of staticFiles) { + const srcFile = path.join(srcDir, file); + const destFile = path.join(distDir, file); + + if (fs.existsSync(srcFile)) { + fs.copyFileSync(srcFile, destFile); + console.log(`Copied ${file}`); + } +} + +// Copy toolbar CSS +const toolbarCssSource = path.join(__dirname, '..', '..', 'toolbar', 'chartifact-toolbar.css'); +const toolbarCssDest = path.join(distDir, 'chartifact-toolbar.css'); +if (fs.existsSync(toolbarCssSource)) { + fs.copyFileSync(toolbarCssSource, toolbarCssDest); + console.log('Copied chartifact-toolbar.css'); +} + +// Copy UMD bundles for use in content script +const docsDir = path.join(__dirname, '..', '..', '..', 'docs', 'dist', 'v1'); +const bundleFiles = [ + 'chartifact.sandbox.umd.js', + 'chartifact.compiler.umd.js', + 'chartifact.host.umd.js' +]; + +for (const file of bundleFiles) { + const srcFile = path.join(docsDir, file); + const destFile = path.join(distDir, file.replace('.umd.js', '.js')); + + if (fs.existsSync(srcFile)) { + fs.copyFileSync(srcFile, destFile); + console.log(`Copied ${file} as ${path.basename(destFile)}`); + } else { + console.warn(`Bundle file not found: ${srcFile}`); + } +} + +// Create icons directory and placeholder icons +const iconsDir = path.join(distDir, 'icons'); +if (!fs.existsSync(iconsDir)) { + fs.mkdirSync(iconsDir, { recursive: true }); +} + +// Create simple text-based placeholder icons (in production, use actual PNG files) +const iconSizes = [16, 32, 48, 128]; +for (const size of iconSizes) { + const iconFile = path.join(iconsDir, `icon-${size}.png`); + + // Create a minimal fake PNG-like file to make Chrome happy + // In production, these should be proper PNG icon files + const header = `PNG ICON ${size}x${size}`; + const padding = Buffer.alloc(Math.max(0, 100 - header.length), 0); + const iconBuffer = Buffer.concat([Buffer.from(header), padding]); + fs.writeFileSync(iconFile, iconBuffer); +} + +console.log('Resources copied successfully'); \ No newline at end of file diff --git a/packages/chrome-extension/src/background.ts b/packages/chrome-extension/src/background.ts new file mode 100644 index 00000000..be7a5b26 --- /dev/null +++ b/packages/chrome-extension/src/background.ts @@ -0,0 +1,26 @@ +/** + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ + +// Background service worker for the Chrome extension +chrome.runtime.onInstalled.addListener(() => { + console.log('Chartifact Interactive Document Viewer installed'); +}); + +// Handle content script messages +chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { + if (request.type === 'IDOC_FILE_DETECTED') { + // Could show notification or update badge + console.log('Interactive Document file detected:', request.url); + sendResponse({ success: true }); + } +}); + +// Handle action button clicks +chrome.action.onClicked.addListener((tab) => { + // Open the viewer for the current tab if it's an idoc file + if (tab.url && (tab.url.includes('.idoc.md') || tab.url.includes('.idoc.json'))) { + chrome.tabs.sendMessage(tab.id!, { type: 'OPEN_VIEWER' }); + } +}); \ No newline at end of file diff --git a/packages/chrome-extension/src/content.ts b/packages/chrome-extension/src/content.ts new file mode 100644 index 00000000..f3ed99d3 --- /dev/null +++ b/packages/chrome-extension/src/content.ts @@ -0,0 +1,302 @@ +/** + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ + +// Content script that runs on pages with .idoc files +(function() { + 'use strict'; + + // Check if this is an Interactive Document file + function isIdocFile(): boolean { + const url = window.location.href; + return url.endsWith('.idoc.md') || url.endsWith('.idoc.json'); + } + + // Get file content from the page + function getFileContent(): string | null { + // For file:// URLs, the content is usually displayed in a
 tag or plain text
+    const pre = document.querySelector('pre');
+    if (pre) {
+      return pre.textContent || '';
+    }
+    
+    // Fallback to body text content
+    return document.body.textContent || '';
+  }
+
+  // Load external scripts
+  function loadScript(src: string): Promise {
+    return new Promise((resolve, reject) => {
+      const script = document.createElement('script');
+      script.src = chrome.runtime.getURL(src);
+      script.onload = () => resolve();
+      script.onerror = reject;
+      document.head.appendChild(script);
+    });
+  }
+
+  // Load CSS file
+  function loadCSS(href: string): void {
+    const link = document.createElement('link');
+    link.rel = 'stylesheet';
+    link.href = chrome.runtime.getURL(href);
+    document.head.appendChild(link);
+  }
+
+  // Inject the viewer interface using host/toolbar architecture
+  async function injectViewer(): Promise {
+    const content = getFileContent();
+    if (!content) {
+      console.warn('Could not extract file content');
+      return;
+    }
+
+    try {
+      // Load required scripts and CSS
+      await Promise.all([
+        loadScript('chartifact.sandbox.js'),
+        loadScript('chartifact.host.js'),
+        loadScript('chartifact.compiler.js')
+      ]);
+      loadCSS('chartifact-toolbar.css');
+
+      // Create overlay container with proper structure
+      const overlay = document.createElement('div');
+      overlay.id = 'chartifact-viewer-overlay';
+      overlay.style.cssText = `
+        position: fixed;
+        top: 0;
+        left: 0;
+        width: 100%;
+        height: 100%;
+        background: white;
+        z-index: 10000;
+        display: flex;
+        flex-direction: column;
+      `;
+
+      // Create toolbar container
+      const toolbarContainer = document.createElement('div');
+      toolbarContainer.className = 'chartifact-toolbar';
+      toolbarContainer.style.cssText = `
+        background: #f5f5f5;
+        border-bottom: 1px solid #ddd;
+        padding: 10px 20px;
+      `;
+
+      // Create close button in toolbar
+      const closeButton = document.createElement('button');
+      closeButton.textContent = '✕ Close Viewer';
+      closeButton.style.cssText = `
+        background: #dc3545;
+        color: white;
+        border: none;
+        padding: 8px 16px;
+        cursor: pointer;
+        border-radius: 4px;
+        float: right;
+        margin-left: 10px;
+      `;
+      closeButton.onclick = () => overlay.remove();
+
+      // Create source textarea (hidden by default)
+      const textarea = document.createElement('textarea');
+      textarea.id = 'source';
+      textarea.style.cssText = `
+        width: 100%;
+        height: 200px;
+        font-family: monospace;
+        display: none;
+        resize: vertical;
+        border: 1px solid #ddd;
+        padding: 10px;
+      `;
+      textarea.value = content;
+
+      // Create main content containers
+      const mainContainer = document.createElement('div');
+      mainContainer.style.cssText = `
+        flex: 1;
+        display: flex;
+        flex-direction: column;
+        overflow: hidden;
+      `;
+
+      const loadingDiv = document.createElement('div');
+      loadingDiv.id = 'loading';
+      loadingDiv.style.cssText = `
+        text-align: center;
+        padding: 40px;
+        font-size: 18px;
+      `;
+      loadingDiv.textContent = 'Loading Interactive Document...';
+
+      const helpDiv = document.createElement('div');
+      helpDiv.id = 'help';
+      helpDiv.style.cssText = `
+        text-align: center;
+        padding: 40px;
+        display: none;
+      `;
+      helpDiv.textContent = 'Help information would go here';
+
+      const previewDiv = document.createElement('div');
+      previewDiv.id = 'preview';
+      previewDiv.style.cssText = `
+        flex: 1;
+        overflow: auto;
+        padding: 20px;
+      `;
+
+      // Assemble the UI
+      toolbarContainer.appendChild(closeButton);
+      mainContainer.appendChild(textarea);
+      mainContainer.appendChild(loadingDiv);
+      mainContainer.appendChild(helpDiv);
+      mainContainer.appendChild(previewDiv);
+      overlay.appendChild(toolbarContainer);
+      overlay.appendChild(mainContainer);
+      document.body.appendChild(overlay);
+
+      // Wait a moment for scripts to be available
+      await new Promise(resolve => setTimeout(resolve, 100));
+
+      // Access the global objects created by the UMD bundles
+      const Chartifact = (window as any).Chartifact;
+      if (!Chartifact || !Chartifact.host || !Chartifact.toolbar) {
+        throw new Error('Chartifact libraries not loaded properly');
+      }
+
+      // Initialize toolbar
+      const toolbar = new Chartifact.toolbar.Toolbar(toolbarContainer, {
+        textarea: textarea,
+        tweakButton: true,
+        downloadButton: true,
+        restartButton: false,
+        mode: window.location.href.endsWith('.idoc.json') ? 'json' : 'markdown',
+        filename: extractFilename(window.location.href)
+      });
+
+      // Initialize host listener
+      const host = new Chartifact.host.Listener({
+        preview: previewDiv,
+        loading: loadingDiv,
+        help: helpDiv,
+        toolbar: toolbar,
+        options: {
+          clipboard: false,
+          dragDrop: false,
+          fileUpload: false,
+          postMessage: false,
+          url: false
+        },
+        onApprove: (message: any) => {
+          // Approve all specs for viewing (no editing capabilities)
+          const { specs } = message;
+          return specs;
+        }
+      });
+
+      // Render the content
+      const url = window.location.href;
+      const filename = extractFilename(url);
+      
+      if (url.endsWith('.idoc.json')) {
+        // Parse JSON and render as interactive document
+        try {
+          const interactiveDocument = JSON.parse(content);
+          host.render(filename, null, interactiveDocument, false);
+        } catch (error) {
+          host.errorHandler(error, 'Failed to parse JSON content');
+        }
+      } else {
+        // Render markdown directly
+        host.render(filename, content, null, false);
+      }
+
+    } catch (error) {
+      console.error('Failed to inject viewer:', error);
+      // Show error message
+      const errorDiv = document.createElement('div');
+      errorDiv.style.cssText = `
+        position: fixed;
+        top: 50%;
+        left: 50%;
+        transform: translate(-50%, -50%);
+        background: white;
+        border: 2px solid red;
+        padding: 20px;
+        border-radius: 8px;
+        z-index: 10001;
+        max-width: 400px;
+      `;
+      errorDiv.innerHTML = `
+        

Error Loading Viewer

+

${error.message}

+ + `; + document.body.appendChild(errorDiv); + } + } + + // Extract filename from URL + function extractFilename(url: string): string { + const path = url.split('/').pop() || 'document'; + return path.replace(/\?.*$/, ''); // Remove query parameters + } + + // Add button to view the file + function addViewButton(): void { + const button = document.createElement('button'); + button.textContent = '📊 View as Interactive Document'; + button.style.cssText = ` + position: fixed; + top: 20px; + right: 20px; + background: #007acc; + color: white; + border: none; + padding: 12px 20px; + cursor: pointer; + border-radius: 6px; + font-size: 14px; + font-weight: bold; + box-shadow: 0 2px 10px rgba(0,0,0,0.2); + z-index: 9999; + `; + button.onclick = injectViewer; + document.body.appendChild(button); + } + + // Initialize the content script + function init(): void { + if (!isIdocFile()) { + return; + } + + // Notify background script (using common message structure) + chrome.runtime.sendMessage({ + type: 'IDOC_FILE_DETECTED', + url: window.location.href + }); + + // Add view button + addViewButton(); + + // Listen for messages from background script + chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { + if (request.type === 'OPEN_VIEWER') { + injectViewer(); + sendResponse({ success: true }); + } + }); + } + + // Run when DOM is ready + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init); + } else { + init(); + } +})(); \ No newline at end of file diff --git a/packages/chrome-extension/src/manifest.json b/packages/chrome-extension/src/manifest.json new file mode 100644 index 00000000..634e60c4 --- /dev/null +++ b/packages/chrome-extension/src/manifest.json @@ -0,0 +1,56 @@ +{ + "manifest_version": 3, + "name": "Chartifact Interactive Document Viewer", + "version": "1.0.0", + "description": "View Interactive Document files (*.idoc.md and *.idoc.json) with rich visualizations", + "permissions": [ + "activeTab", + "storage" + ], + "host_permissions": [ + "file://*/*.idoc.md", + "file://*/*.idoc.json", + "https://*/*.idoc.md", + "https://*/*.idoc.json", + "http://*/*.idoc.md", + "http://*/*.idoc.json" + ], + "background": { + "service_worker": "background.js" + }, + "content_scripts": [ + { + "matches": [ + "file://*/*.idoc.md", + "file://*/*.idoc.json", + "https://*/*.idoc.md", + "https://*/*.idoc.json", + "http://*/*.idoc.md", + "http://*/*.idoc.json" + ], + "js": ["content.js"], + "run_at": "document_end" + } + ], + "action": { + "default_popup": "popup.html", + "default_title": "Chartifact Viewer" + }, + "web_accessible_resources": [ + { + "resources": [ + "chartifact.compiler.js", + "chartifact.sandbox.js", + "chartifact.host.js", + "chartifact-toolbar.css" + ], + "matches": [""] + } + ], + "icons": { + "16": "icons/icon-16.png", + "32": "icons/icon-32.png", + "48": "icons/icon-48.png", + "128": "icons/icon-128.png" + } +} \ No newline at end of file diff --git a/packages/chrome-extension/src/popup.html b/packages/chrome-extension/src/popup.html new file mode 100644 index 00000000..d9e90ada --- /dev/null +++ b/packages/chrome-extension/src/popup.html @@ -0,0 +1,119 @@ + + + + + Chartifact Viewer + + + +
+ +

Chartifact

+

Interactive Document Viewer

+
+ +
+
+ Checking current page... +
+ +
+ + +
+
+ +
+ This extension works with .idoc.md and .idoc.json files. Navigate to an Interactive Document file to view it with rich visualizations. +
+ + + + \ No newline at end of file diff --git a/packages/chrome-extension/src/popup.ts b/packages/chrome-extension/src/popup.ts new file mode 100644 index 00000000..19379230 --- /dev/null +++ b/packages/chrome-extension/src/popup.ts @@ -0,0 +1,70 @@ +/** + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ + +// Popup script for the Chrome extension +document.addEventListener('DOMContentLoaded', async () => { + const statusEl = document.getElementById('status')!; + const openViewerBtn = document.getElementById('openViewer')! as HTMLButtonElement; + const refreshStatusBtn = document.getElementById('refreshStatus')! as HTMLButtonElement; + + // Check if current tab has an idoc file + async function checkCurrentTab(): Promise<{ isIdocFile: boolean; url: string }> { + try { + const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); + const url = tab.url || ''; + const isIdocFile = url.endsWith('.idoc.md') || url.endsWith('.idoc.json'); + return { isIdocFile, url }; + } catch (error) { + console.error('Error checking current tab:', error); + return { isIdocFile: false, url: '' }; + } + } + + // Update the status display + function updateStatus(isIdocFile: boolean, url: string): void { + if (isIdocFile) { + statusEl.className = 'status detected'; + statusEl.textContent = '✅ Interactive Document detected!'; + openViewerBtn.disabled = false; + } else { + statusEl.className = 'status not-detected'; + if (url.startsWith('chrome://') || url.startsWith('chrome-extension://')) { + statusEl.textContent = '📋 Navigate to an .idoc file to use the viewer'; + } else { + statusEl.textContent = '📄 No Interactive Document on this page'; + } + openViewerBtn.disabled = true; + } + } + + // Open the viewer on the current tab + async function openViewer(): Promise { + try { + const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); + if (tab.id) { + await chrome.tabs.sendMessage(tab.id, { type: 'OPEN_VIEWER' }); + window.close(); // Close the popup + } + } catch (error) { + console.error('Error opening viewer:', error); + statusEl.className = 'status not-detected'; + statusEl.textContent = '❌ Error opening viewer'; + } + } + + // Refresh the status + async function refreshStatus(): Promise { + statusEl.textContent = 'Checking...'; + const { isIdocFile, url } = await checkCurrentTab(); + updateStatus(isIdocFile, url); + } + + // Event listeners + openViewerBtn.addEventListener('click', openViewer); + refreshStatusBtn.addEventListener('click', refreshStatus); + + // Initial status check + await refreshStatus(); +}); \ No newline at end of file diff --git a/packages/chrome-extension/test-extension.html b/packages/chrome-extension/test-extension.html new file mode 100644 index 00000000..d994a8d8 --- /dev/null +++ b/packages/chrome-extension/test-extension.html @@ -0,0 +1,49 @@ + + + + Test Chrome Extension - test-simple.idoc.md + + +
# Simple Test Document
+
+This is a test Interactive Document.
+
+```echarts
+{
+  "title": { "text": "Simple Chart" },
+  "xAxis": { "type": "category", "data": ["A", "B", "C"] },
+  "yAxis": { "type": "value" },
+  "series": [{ "data": [10, 20, 30], "type": "bar" }]
+}
+```
+ + + + + + + \ No newline at end of file diff --git a/packages/chrome-extension/test-simple.idoc.md b/packages/chrome-extension/test-simple.idoc.md new file mode 100644 index 00000000..0113e1d7 --- /dev/null +++ b/packages/chrome-extension/test-simple.idoc.md @@ -0,0 +1,12 @@ +# Simple Test Document + +This is a test Interactive Document. + +```echarts +{ + "title": { "text": "Simple Chart" }, + "xAxis": { "type": "category", "data": ["A", "B", "C"] }, + "yAxis": { "type": "value" }, + "series": [{ "data": [10, 20, 30], "type": "bar" }] +} +``` \ No newline at end of file diff --git a/packages/chrome-extension/test.html b/packages/chrome-extension/test.html new file mode 100644 index 00000000..b0b4ac08 --- /dev/null +++ b/packages/chrome-extension/test.html @@ -0,0 +1,84 @@ + + + + Interactive Document Test + + + +
+

Chartifact Chrome Extension Test Page

+

This page helps you test the Chartifact Chrome extension with sample Interactive Document files.

+
+ +
+

📋 Instructions

+
    +
  1. Install the Chrome extension by loading the packages/chrome-extension/dist folder as an unpacked extension
  2. +
  3. Click one of the test files below
  4. +
  5. Look for the "📊 View as Interactive Document" button in the top-right corner
  6. +
  7. Click the button to open the Chartifact viewer
  8. +
+
+ +

Test Files

+ + +

Extension Status

+

If the extension is properly installed and enabled, it will:

+
    +
  • Automatically detect .idoc.md and .idoc.json files
  • +
  • Show a floating "View as Interactive Document" button
  • +
  • Allow you to open an overlay viewer with rich visualizations
  • +
+ +

Troubleshooting

+
    +
  • Button not appearing? Make sure the extension is enabled and has permissions
  • +
  • Viewer not loading? Check the browser console for errors
  • +
  • Charts not rendering? Ensure the file content is valid Interactive Document format
  • +
+ +
+

Note: This is a development test page. In production, the extension would work with any .idoc files hosted on the web or viewed locally via file:// URLs.

+
+ + \ No newline at end of file diff --git a/packages/chrome-extension/test.idoc.json b/packages/chrome-extension/test.idoc.json new file mode 100644 index 00000000..5f24734f --- /dev/null +++ b/packages/chrome-extension/test.idoc.json @@ -0,0 +1,37 @@ +{ + "$schema": "https://raw.githubusercontent.com/microsoft/chartifact/main/docs/schema/idoc_v1.json", + "title": "Chrome Extension Test", + "dataLoaders": [ + { + "type": "data", + "data": [ + { "category": "A", "value": 28 }, + { "category": "B", "value": 55 }, + { "category": "C", "value": 43 } + ], + "dataSourceName": "testData" + } + ], + "groups": [ + { + "groupId": "main", + "elements": [ + "# Chrome Extension Test", + "This is a test Interactive Document for the Chrome extension.", + { + "elementId": "chart1", + "type": "chart", + "dataSource": "testData", + "spec": { + "$schema": "https://vega.github.io/schema/vega-lite/v5.json", + "mark": "bar", + "encoding": { + "x": { "field": "category", "type": "nominal" }, + "y": { "field": "value", "type": "quantitative" } + } + } + } + ] + } + ] +} \ No newline at end of file diff --git a/packages/chrome-extension/tsconfig.json b/packages/chrome-extension/tsconfig.json new file mode 100644 index 00000000..847b74d9 --- /dev/null +++ b/packages/chrome-extension/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist", + "module": "esnext", + "moduleResolution": "bundler", + "target": "es2020", + "lib": ["es2020", "dom"], + "types": ["chrome"], + "allowSyntheticDefaultImports": true, + "esModuleInterop": true + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "dist", + "node_modules" + ] +} \ No newline at end of file diff --git a/packages/chrome-extension/vite.config.js b/packages/chrome-extension/vite.config.js new file mode 100644 index 00000000..b0850fbc --- /dev/null +++ b/packages/chrome-extension/vite.config.js @@ -0,0 +1,29 @@ +import { defineConfig } from 'vite'; +import { resolve } from 'path'; + +export default defineConfig({ + build: { + outDir: 'dist', + rollupOptions: { + input: { + background: resolve(__dirname, 'src/background.ts'), + content: resolve(__dirname, 'src/content.ts'), + popup: resolve(__dirname, 'src/popup.ts'), + }, + output: { + entryFileNames: '[name].js', + format: 'es' + }, + external: [ + '@microsoft/chartifact-sandbox', + '@microsoft/chartifact-compiler' + ] + }, + target: 'es2020', + minify: false, // Keep readable for debugging + sourcemap: false + }, + define: { + 'process.env.NODE_ENV': '"production"' + } +}); \ No newline at end of file