diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index 8c153b0..f6ffce7 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -33,17 +33,21 @@ jobs: MDBOOK_EXTERNAL_LINKS_VERSION: 0.1.2 steps: - uses: actions/checkout@v4 + - name: Setup mise tools + uses: jdx/mise-action@c37c93293d6b742fc901e1406b8f764f6fb19dac # v2.4.4 + - name: Install dependencies + run: pnpm install --frozen-lockfile - name: Install mdBook run: | - curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf -y | sh - rustup update cargo install --version ${MDBOOK_VERSION} mdbook cargo install --version ${MDBOOK_EXTERNAL_LINKS_VERSION} mdbook-external-links + - name: Create docs tables + run: npm run docs:create-tables + - name: Build with mdBook + run: mdbook build - name: Setup Pages id: pages uses: actions/configure-pages@v5 - - name: Build with mdBook - run: mdbook build - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: @@ -59,4 +63,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 \ No newline at end of file + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index 7585238..f0db70f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,8 @@ book + +# TypeScript +node_modules +dist +.npmrc +.nx +*.tsbuildinfo diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..3f304fe --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,2 @@ +#!/bin/sh +npm run docs:validate diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..4c782e7 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +# Ignore markdown files +/src diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..042671e --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "printWidth": 99, + "tabWidth": 4, + "semi": true, + "singleQuote": true, + "trailingComma": "es5" +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..9b1fa16 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "editor.defaultFormatter": "esbenp.prettier-vscode", + "search.exclude": { + "**/.git": true, + }, + "terminal.integrated.defaultProfile.osx": "zsh - work", + "js/ts.preferences.includePackageJsonAutoImports": "on", + "cSpell.words": [ + "mdbook" + ] +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..b32f85c --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,76 @@ +# AGENTS.md + +## Repository purpose + +This repository contains example Grasshopper models and their documentation for ShapeDiver. Documentation is built with mdBook from the `src/` directory. + +## Important paths + +- `src/**/examples.json` — source of truth for example metadata +- `src/**/definitions.md` — chapter documentation with human-written prose and generated tables +- `tools/docs-cli/src/utils.ts` — defines `SCHEMA_EXAMPLES` +- `tools/docs-cli/src/validate.ts` — validation logic +- `tools/docs-cli/src/createTables.ts` — markdown table generation +- `book.toml` — mdBook configuration + +## Source of truth + +Treat these as the canonical sources: + +- `src/**/examples.json` +- human-written prose in `src/**/definitions.md` +- existing model files provided by humans + +Treat generated markdown tables in `src/**/definitions.md` as derived output. + +## Hard rules + +- Never manually edit generated markdown tables in `src/**/definitions.md`. +- If table content must change, update `src/**/examples.json` instead. +- Never create, modify, or rewrite `.ghx`, `.gh`, or `.3dm` files. +- Do not rename, move, or delete large groups of examples or docs unless the user explicitly asks. +- Avoid broad structural reorganizations of chapter folders or documentation layout unless explicitly asked. + +## Editing guidance + +- You may add or edit explanatory prose in `src/**/definitions.md`. +- Preserve mdBook structure and existing chapter organization unless the user asks for restructuring. +- AI may help develop repository code, including the docs CLI and related automation. + +## Adding or updating examples + +Preferred workflow: + +1. Use the existing human-provided model artifact in the appropriate chapter folder. +2. Add or update the matching entry in that chapter's `src/**/examples.json`. +3. Add explanatory prose to `definitions.md` only when needed. +4. Do not manually regenerate or rewrite markdown tables unless the user explicitly asks for that local workflow. + +## Naming convention + +Example files follow this pattern: + +`{chapter number}{alphabetical identifier starting from A}-{description}.{extension}` + +Example: + +`11A-AppBuilder_Tutorial1.ghx` + +## Validation + +After changing any `src/**/examples.json`, run: + +```bash +npm run docs:validate +``` + +This validation is required, but it does not check everything. If your change affects behavior or content not fully covered by the validation script, explicitly warn the user about the remaining unchecked risk. + +## Useful commands + +```bash +npm run docs:validate +mdbook serve . +``` + +Use `npm run docs:validate` for metadata validation and `mdbook serve .` for local documentation preview. diff --git a/README.md b/README.md index 045a098..fb11df9 100644 --- a/README.md +++ b/README.md @@ -9,15 +9,31 @@ You can clone this repository to download all models at once. ### Overview -This repository uses [mdBook](https://rust-lang.github.io/mdBook/) to auto-generate documentation based on [Markdown](https://rust-lang.github.io/mdBook/format/markdown.html). A [GitHub action](.github/workflows/static.yml) is used to deploy to [GitHub pages](https://shapediver.github.io/GrasshopperExampleModels/). +This repository uses [mdBook](https://rust-lang.github.io/mdBook/) to auto-generate documentation based on [Markdown](https://rust-lang.github.io/mdBook/format/markdown.html). A [GitHub action](.github/workflows/static.yml) is used to deploy to [GitHub pages](https://shapediver.github.io/GrasshopperExampleModels/). + +The source of truth for example metadata is `src/**/examples.json`. Markdown tables in `src/**/definitions.md` are generated from these JSON files and should not be created or edited manually. + +### Repository structure + +- `src/` contains the mdBook content, example metadata, and model files. +- `tools/` contains repository tooling. In particular, `tools/docs-cli/` provides the scripts used to validate `examples.json` files and generate markdown tables from them. ### Guidelines * Use the `ghx` XML file format of Grasshopper. + * Update `examples.json` when changing example metadata. + * Add only additional explanatory text manually to Markdown files. + * Do not manually create or edit generated markdown tables; they are generated via GitHub workflows. + +### Commands + +- `npm run docs:validate` validates all `src/**/examples.json` files and checks referenced files. +- `npm run docs:create-tables` regenerates markdown tables from `src/**/examples.json`. +- `mdbook serve .` starts a local preview server for the documentation. ### Local testing -Take the following setup steps to locally test auto-generation of the documentation. +Take the following setup steps to locally test auto-generation of the documentation. * Install [rust](https://www.rust-lang.org/tools/install) * Install [mdBook](https://rust-lang.github.io/mdBook/guide/installation.html) @@ -26,3 +42,5 @@ Take the following setup steps to locally test auto-generation of the documentat `cargo install mdbook-external-links` * From the root directory of this repository run `mdbook serve .` +In normal workflows, markdown tables are generated through GitHub workflows and should not be created manually. + diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..0e981fd --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,5 @@ +import { defineConfig } from 'eslint/config'; +import eslint from '@eslint/js'; +import tseslint from 'typescript-eslint'; + +export default defineConfig([eslint.configs.recommended, ...tseslint.configs.recommended]); diff --git a/mise.toml b/mise.toml new file mode 100644 index 0000000..8e4c250 --- /dev/null +++ b/mise.toml @@ -0,0 +1,4 @@ +[tools] +node = "24" +pnpm = "11" +rust = "stable" diff --git a/package.json b/package.json new file mode 100644 index 0000000..1108068 --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "root", + "private": true, + "scripts": { + "prepare": "husky", + "docs:create-tables": "pnpm --filter @shapediver/docs-cli run create-tables \"${PWD}/src/\"", + "docs:validate": "pnpm --filter @shapediver/docs-cli run validate \"${PWD}/src/\"" + }, + "devDependencies": { + "@eslint/js": "10.0.1", + "eslint": "10.2.0", + "husky": "9.1.7", + "npm-check-updates": "21.0.0", + "prettier": "3.8.3", + "typescript-eslint": "8.58.2" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..35d887a --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,1152 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@eslint/js': + specifier: 10.0.1 + version: 10.0.1(eslint@10.2.0) + eslint: + specifier: 10.2.0 + version: 10.2.0 + husky: + specifier: 9.1.7 + version: 9.1.7 + npm-check-updates: + specifier: 21.0.0 + version: 21.0.0 + prettier: + specifier: 3.8.3 + version: 3.8.3 + typescript-eslint: + specifier: 8.58.2 + version: 8.58.2(eslint@10.2.0)(typescript@6.0.2) + + tools/docs-cli: + dependencies: + zod: + specifier: 4.3.6 + version: 4.3.6 + devDependencies: + '@types/node': + specifier: 24.1.0 + version: 24.1.0 + tsx: + specifier: 4.21.0 + version: 4.21.0 + typescript: + specifier: 6.0.2 + version: 6.0.2 + +packages: + + '@esbuild/aix-ppc64@0.27.7': + resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.7': + resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.7': + resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.7': + resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.7': + resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.7': + resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.7': + resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.7': + resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.7': + resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.7': + resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.7': + resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.7': + resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.7': + resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.7': + resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.7': + resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.7': + resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.7': + resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.7': + resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.7': + resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.7': + resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.7': + resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.7': + resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.7': + resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.7': + resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.7': + resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.7': + resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.23.5': + resolution: {integrity: sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/config-helpers@0.5.5': + resolution: {integrity: sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/core@1.2.1': + resolution: {integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/js@10.0.1': + resolution: {integrity: sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + peerDependencies: + eslint: ^10.0.0 + peerDependenciesMeta: + eslint: + optional: true + + '@eslint/object-schema@3.0.5': + resolution: {integrity: sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/plugin-kit@0.7.1': + resolution: {integrity: sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@types/esrecurse@4.3.1': + resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/node@24.1.0': + resolution: {integrity: sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==} + + '@typescript-eslint/eslint-plugin@8.58.2': + resolution: {integrity: sha512-aC2qc5thQahutKjP+cl8cgN9DWe3ZUqVko30CMSZHnFEHyhOYoZSzkGtAI2mcwZ38xeImDucI4dnqsHiOYuuCw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.58.2 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/parser@8.58.2': + resolution: {integrity: sha512-/Zb/xaIDfxeJnvishjGdcR4jmr7S+bda8PKNhRGdljDM+elXhlvN0FyPSsMnLmJUrVG9aPO6dof80wjMawsASg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/project-service@8.58.2': + resolution: {integrity: sha512-Cq6UfpZZk15+r87BkIh5rDpi38W4b+Sjnb8wQCPPDDweS/LRCFjCyViEbzHk5Ck3f2QDfgmlxqSa7S7clDtlfg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/scope-manager@8.58.2': + resolution: {integrity: sha512-SgmyvDPexWETQek+qzZnrG6844IaO02UVyOLhI4wpo82dpZJY9+6YZCKAMFzXb7qhx37mFK1QcPQ18tud+vo6Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.58.2': + resolution: {integrity: sha512-3SR+RukipDvkkKp/d0jP0dyzuls3DbGmwDpVEc5wqk5f38KFThakqAAO0XMirWAE+kT00oTauTbzMFGPoAzB0A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/type-utils@8.58.2': + resolution: {integrity: sha512-Z7EloNR/B389FvabdGeTo2XMs4W9TjtPiO9DAsmT0yom0bwlPyRjkJ1uCdW1DvrrrYP50AJZ9Xc3sByZA9+dcg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.58.2': + resolution: {integrity: sha512-9TukXyATBQf/Jq9AMQXfvurk+G5R2MwfqQGDR2GzGz28HvY/lXNKGhkY+6IOubwcquikWk5cjlgPvD2uAA7htQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.58.2': + resolution: {integrity: sha512-ELGuoofuhhoCvNbQjFFiobFcGgcDCEm0ThWdmO4Z0UzLqPXS3KFvnEZ+SHewwOYHjM09tkzOWXNTv9u6Gqtyuw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.58.2': + resolution: {integrity: sha512-QZfjHNEzPY8+l0+fIXMvuQ2sJlplB4zgDZvA+NmvZsZv3EQwOcc1DuIU1VJUTWZ/RKouBMhDyNaBMx4sWvrzRA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.58.2': + resolution: {integrity: sha512-f1WO2Lx8a9t8DARmcWAUPJbu0G20bJlj8L4z72K00TMeJAoyLr/tHhI/pzYBLrR4dXWkcxO1cWYZEOX8DKHTqA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.14.0: + resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + brace-expansion@5.0.5: + resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} + engines: {node: 18 || 20 || >=22} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + esbuild@0.27.7: + resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} + engines: {node: '>=18'} + hasBin: true + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-scope@9.1.2: + resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@10.2.0: + resolution: {integrity: sha512-+L0vBFYGIpSNIt/KWTpFonPrqYvgKw1eUI5Vn7mEogrQcWtWYtNQ7dNqC+px/J0idT3BAkiWrhfS7k+Tum8TUA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@11.2.0: + resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + get-tsconfig@4.14.0: + resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + husky@9.1.7: + resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} + engines: {node: '>=18'} + hasBin: true + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + npm-check-updates@21.0.0: + resolution: {integrity: sha512-iGFLoW1QWsEDLR6Cnklyk+iHTf20hS84o79idR6AKhjSwk0whMdCd5FS0bTgEe6gMrRnJ0fGr2P6BEZ2zOelYg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: '>=10.0.0'} + hasBin: true + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier@3.8.3: + resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==} + engines: {node: '>=14'} + hasBin: true + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + tinyglobby@0.2.16: + resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + engines: {node: '>=12.0.0'} + + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tsx@4.21.0: + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + engines: {node: '>=18.0.0'} + hasBin: true + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typescript-eslint@8.58.2: + resolution: {integrity: sha512-V8iSng9mRbdZjl54VJ9NKr6ZB+dW0J3TzRXRGcSbLIej9jV86ZRtlYeTKDR/QLxXykocJ5icNzbsl2+5TzIvcQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + typescript@6.0.2: + resolution: {integrity: sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@7.8.0: + resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zod@4.3.6: + resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + +snapshots: + + '@esbuild/aix-ppc64@0.27.7': + optional: true + + '@esbuild/android-arm64@0.27.7': + optional: true + + '@esbuild/android-arm@0.27.7': + optional: true + + '@esbuild/android-x64@0.27.7': + optional: true + + '@esbuild/darwin-arm64@0.27.7': + optional: true + + '@esbuild/darwin-x64@0.27.7': + optional: true + + '@esbuild/freebsd-arm64@0.27.7': + optional: true + + '@esbuild/freebsd-x64@0.27.7': + optional: true + + '@esbuild/linux-arm64@0.27.7': + optional: true + + '@esbuild/linux-arm@0.27.7': + optional: true + + '@esbuild/linux-ia32@0.27.7': + optional: true + + '@esbuild/linux-loong64@0.27.7': + optional: true + + '@esbuild/linux-mips64el@0.27.7': + optional: true + + '@esbuild/linux-ppc64@0.27.7': + optional: true + + '@esbuild/linux-riscv64@0.27.7': + optional: true + + '@esbuild/linux-s390x@0.27.7': + optional: true + + '@esbuild/linux-x64@0.27.7': + optional: true + + '@esbuild/netbsd-arm64@0.27.7': + optional: true + + '@esbuild/netbsd-x64@0.27.7': + optional: true + + '@esbuild/openbsd-arm64@0.27.7': + optional: true + + '@esbuild/openbsd-x64@0.27.7': + optional: true + + '@esbuild/openharmony-arm64@0.27.7': + optional: true + + '@esbuild/sunos-x64@0.27.7': + optional: true + + '@esbuild/win32-arm64@0.27.7': + optional: true + + '@esbuild/win32-ia32@0.27.7': + optional: true + + '@esbuild/win32-x64@0.27.7': + optional: true + + '@eslint-community/eslint-utils@4.9.1(eslint@10.2.0)': + dependencies: + eslint: 10.2.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.23.5': + dependencies: + '@eslint/object-schema': 3.0.5 + debug: 4.4.3 + minimatch: 10.2.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.5.5': + dependencies: + '@eslint/core': 1.2.1 + + '@eslint/core@1.2.1': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/js@10.0.1(eslint@10.2.0)': + optionalDependencies: + eslint: 10.2.0 + + '@eslint/object-schema@3.0.5': {} + + '@eslint/plugin-kit@0.7.1': + dependencies: + '@eslint/core': 1.2.1 + levn: 0.4.1 + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.7': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@types/esrecurse@4.3.1': {} + + '@types/estree@1.0.8': {} + + '@types/json-schema@7.0.15': {} + + '@types/node@24.1.0': + dependencies: + undici-types: 7.8.0 + + '@typescript-eslint/eslint-plugin@8.58.2(@typescript-eslint/parser@8.58.2(eslint@10.2.0)(typescript@6.0.2))(eslint@10.2.0)(typescript@6.0.2)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.58.2(eslint@10.2.0)(typescript@6.0.2) + '@typescript-eslint/scope-manager': 8.58.2 + '@typescript-eslint/type-utils': 8.58.2(eslint@10.2.0)(typescript@6.0.2) + '@typescript-eslint/utils': 8.58.2(eslint@10.2.0)(typescript@6.0.2) + '@typescript-eslint/visitor-keys': 8.58.2 + eslint: 10.2.0 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@6.0.2) + typescript: 6.0.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.58.2(eslint@10.2.0)(typescript@6.0.2)': + dependencies: + '@typescript-eslint/scope-manager': 8.58.2 + '@typescript-eslint/types': 8.58.2 + '@typescript-eslint/typescript-estree': 8.58.2(typescript@6.0.2) + '@typescript-eslint/visitor-keys': 8.58.2 + debug: 4.4.3 + eslint: 10.2.0 + typescript: 6.0.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.58.2(typescript@6.0.2)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.58.2(typescript@6.0.2) + '@typescript-eslint/types': 8.58.2 + debug: 4.4.3 + typescript: 6.0.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.58.2': + dependencies: + '@typescript-eslint/types': 8.58.2 + '@typescript-eslint/visitor-keys': 8.58.2 + + '@typescript-eslint/tsconfig-utils@8.58.2(typescript@6.0.2)': + dependencies: + typescript: 6.0.2 + + '@typescript-eslint/type-utils@8.58.2(eslint@10.2.0)(typescript@6.0.2)': + dependencies: + '@typescript-eslint/types': 8.58.2 + '@typescript-eslint/typescript-estree': 8.58.2(typescript@6.0.2) + '@typescript-eslint/utils': 8.58.2(eslint@10.2.0)(typescript@6.0.2) + debug: 4.4.3 + eslint: 10.2.0 + ts-api-utils: 2.5.0(typescript@6.0.2) + typescript: 6.0.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.58.2': {} + + '@typescript-eslint/typescript-estree@8.58.2(typescript@6.0.2)': + dependencies: + '@typescript-eslint/project-service': 8.58.2(typescript@6.0.2) + '@typescript-eslint/tsconfig-utils': 8.58.2(typescript@6.0.2) + '@typescript-eslint/types': 8.58.2 + '@typescript-eslint/visitor-keys': 8.58.2 + debug: 4.4.3 + minimatch: 10.2.5 + semver: 7.7.4 + tinyglobby: 0.2.16 + ts-api-utils: 2.5.0(typescript@6.0.2) + typescript: 6.0.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.58.2(eslint@10.2.0)(typescript@6.0.2)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@10.2.0) + '@typescript-eslint/scope-manager': 8.58.2 + '@typescript-eslint/types': 8.58.2 + '@typescript-eslint/typescript-estree': 8.58.2(typescript@6.0.2) + eslint: 10.2.0 + typescript: 6.0.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.58.2': + dependencies: + '@typescript-eslint/types': 8.58.2 + eslint-visitor-keys: 5.0.1 + + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn@8.16.0: {} + + ajv@6.14.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + balanced-match@4.0.4: {} + + brace-expansion@5.0.5: + dependencies: + balanced-match: 4.0.4 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + esbuild@0.27.7: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.7 + '@esbuild/android-arm': 0.27.7 + '@esbuild/android-arm64': 0.27.7 + '@esbuild/android-x64': 0.27.7 + '@esbuild/darwin-arm64': 0.27.7 + '@esbuild/darwin-x64': 0.27.7 + '@esbuild/freebsd-arm64': 0.27.7 + '@esbuild/freebsd-x64': 0.27.7 + '@esbuild/linux-arm': 0.27.7 + '@esbuild/linux-arm64': 0.27.7 + '@esbuild/linux-ia32': 0.27.7 + '@esbuild/linux-loong64': 0.27.7 + '@esbuild/linux-mips64el': 0.27.7 + '@esbuild/linux-ppc64': 0.27.7 + '@esbuild/linux-riscv64': 0.27.7 + '@esbuild/linux-s390x': 0.27.7 + '@esbuild/linux-x64': 0.27.7 + '@esbuild/netbsd-arm64': 0.27.7 + '@esbuild/netbsd-x64': 0.27.7 + '@esbuild/openbsd-arm64': 0.27.7 + '@esbuild/openbsd-x64': 0.27.7 + '@esbuild/openharmony-arm64': 0.27.7 + '@esbuild/sunos-x64': 0.27.7 + '@esbuild/win32-arm64': 0.27.7 + '@esbuild/win32-ia32': 0.27.7 + '@esbuild/win32-x64': 0.27.7 + + escape-string-regexp@4.0.0: {} + + eslint-scope@9.1.2: + dependencies: + '@types/esrecurse': 4.3.1 + '@types/estree': 1.0.8 + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@10.2.0: + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@10.2.0) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.23.5 + '@eslint/config-helpers': 0.5.5 + '@eslint/core': 1.2.1 + '@eslint/plugin-kit': 0.7.1 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + ajv: 6.14.0 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 9.1.2 + eslint-visitor-keys: 5.0.1 + espree: 11.2.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + minimatch: 10.2.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@11.2.0: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 5.0.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + fast-deep-equal@3.1.3: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.2 + keyv: 4.5.4 + + flatted@3.4.2: {} + + fsevents@2.3.3: + optional: true + + get-tsconfig@4.14.0: + dependencies: + resolve-pkg-maps: 1.0.0 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + husky@9.1.7: {} + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + imurmurhash@0.1.4: {} + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + isexe@2.0.0: {} + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.5 + + ms@2.1.3: {} + + natural-compare@1.4.0: {} + + npm-check-updates@21.0.0: {} + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + picomatch@4.0.4: {} + + prelude-ls@1.2.1: {} + + prettier@3.8.3: {} + + punycode@2.3.1: {} + + resolve-pkg-maps@1.0.0: {} + + semver@7.7.4: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + tinyglobby@0.2.16: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + ts-api-utils@2.5.0(typescript@6.0.2): + dependencies: + typescript: 6.0.2 + + tsx@4.21.0: + dependencies: + esbuild: 0.27.7 + get-tsconfig: 4.14.0 + optionalDependencies: + fsevents: 2.3.3 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typescript-eslint@8.58.2(eslint@10.2.0)(typescript@6.0.2): + dependencies: + '@typescript-eslint/eslint-plugin': 8.58.2(@typescript-eslint/parser@8.58.2(eslint@10.2.0)(typescript@6.0.2))(eslint@10.2.0)(typescript@6.0.2) + '@typescript-eslint/parser': 8.58.2(eslint@10.2.0)(typescript@6.0.2) + '@typescript-eslint/typescript-estree': 8.58.2(typescript@6.0.2) + '@typescript-eslint/utils': 8.58.2(eslint@10.2.0)(typescript@6.0.2) + eslint: 10.2.0 + typescript: 6.0.2 + transitivePeerDependencies: + - supports-color + + typescript@6.0.2: {} + + undici-types@7.8.0: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + yocto-queue@0.1.0: {} + + zod@4.3.6: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..58a0f16 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,4 @@ +packages: + - 'tools/docs-cli/' +allowBuilds: + esbuild: true diff --git a/src/01-Basics/definitions.md b/src/01-Basics/definitions.md index 612636c..011dfe2 100644 --- a/src/01-Basics/definitions.md +++ b/src/01-Basics/definitions.md @@ -1,7 +1,3 @@ # Basics - -| Ref | Description | ShapeDiver links | Grasshopper definition | Level     | -|---------|---------|---------|---------|---------| -| **1A** | Basic example without the ShapeDiver plugin | [Model](https://www.shapediver.com/app/m/1a-examplewithoutshapediverplugin-110) | [Download](1A-ExampleWithoutShapeDiverPlugin.ghx) | ★ ☆ ☆ | -| **1B** | Basic example with the ShapeDiver plugin | [Model](https://www.shapediver.com/app/m/1b-examplewithshapediverplugin-108) | [Download](1B-ExampleWithShapeDiverPlugin.ghx) | ★ ☆ ☆ | \ No newline at end of file +[basics]: # diff --git a/src/01-Basics/examples.json b/src/01-Basics/examples.json new file mode 100644 index 0000000..68f6f5b --- /dev/null +++ b/src/01-Basics/examples.json @@ -0,0 +1,26 @@ +{ + "1A-ExampleWithoutShapeDiverPlugin.ghx": { + "anchor": "basics", + "slug": "1a-examplewithoutshapediverplugin-110", + "title": "Basic example without the ShapeDiver plugin", + "modelLink": true, + "appLink": false, + "rating": 1, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "1B-ExampleWithShapeDiverPlugin.ghx": { + "anchor": "basics", + "slug": "1b-examplewithshapediverplugin-108", + "title": "Basic example with the ShapeDiver plugin", + "modelLink": true, + "appLink": false, + "rating": 1, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + } +} diff --git a/src/02-Inputs/definitions.md b/src/02-Inputs/definitions.md index fbf203d..d6808f9 100644 --- a/src/02-Inputs/definitions.md +++ b/src/02-Inputs/definitions.md @@ -1,17 +1,3 @@ # Inputs - -| Ref | Resources | Description | ShapeDiver links | Grasshopper definition | Level     | -|---------|---------|---------|---------|---------|---------| -| **2A** | [📖](https://help.shapediver.com/doc/direct-text-input) | Direct Text Input | [Model](https://www.shapediver.com/app/m/2a-directtextinput-27) | [Download](2A-DirectTextInput.ghx) | ★ ☆ ☆ | -| **2B** | [📖](https://help.shapediver.com/doc/import-components) [🎥](https://www.youtube.com/watch?v=6rZiZflGrRo) | File Imports | [Model](https://www.shapediver.com/app/m/2b-fileimports) | [Download](2B-FileImports.ghx) | ★ ☆ ☆ | -| **2C** | | Import document properties and User Text using ShapeDiver Attributes. | [Model](https://www.shapediver.com/app/m/2c-importwithattributes) | [Download](2C-ImportWithAttributes.ghx) / [Input](2C-ImportWithAttributes.3dm) | ★ ★ ☆ | -| **2D** | [📖](https://help.shapediver.com/doc/selection-input) [🎥](https://www.youtube.com/watch?v=m9BsPMvKPDo) | Selection Input basic tutorial | [Model]() / [App](https://appbuilder.shapediver.com/v1/main/latest/?slug=2d-selectioninput-tutorial1) | [Download](2D-SelectionInput_Basic.ghx) | ★ ☆ ☆ | -| **2E** | [📖](https://help.shapediver.com/doc/selection-input) [🎥](https://www.youtube.com/watch?v=m9BsPMvKPDo) | Use a Selection Input to influence both geometry and build a contextual App user interface | [Model]() / [App](https://appbuilder.shapediver.com/v1/main/latest/?slug=2e-selectioninput-tutorial2) | [Download](2E-SelectionInput_Advanced.ghx) | ★ ★ ☆ | -| **2F** | [📖](https://help.shapediver.com/doc/gumball-input) | Gumball Input basic tutorial | [Model](https://www.shapediver.com/app/m/2f-minimalgumballexample) / [App](https://appbuilder.shapediver.com/v1/main/latest/?slug=2f-minimalgumballexample) | [Download](2F-GumballInput_Basic.ghx) | ★ ☆ ☆ | -| **2G** | [📖](https://help.shapediver.com/doc/gumball-input) [🎥](https://www.youtube.com/watch?v=Pu9nDFJXZC0) | Use a Gumball Input to transform geometry in the viewer and apply the transformations in Grasshopper | [Model](https://www.shapediver.com/app/m/2g-gumballinput-tutorial1) / [App](https://appbuilder.shapediver.com/v1/main/latest/?slug=2g-gumballinput-tutorial1) | [Download](2G-GumballInput_Tutorial1.ghx) | ★ ★ ☆ | -| **2H** | [📖](https://help.shapediver.com/doc/gumball-input) | Advanced Gumball example with dynamic transformable objects. | [Model](https://www.shapediver.com/app/m/2h-gumballinput-tutorial2-3) / [App](https://www.shapediver.com/app/builder/v1/main/latest/?slug=2h-gumballinput-tutorial2-3&redirect=0) | [Download](2H-Gumballinput_Tutorial2.ghx) | ★ ★ ★ | -| **2I** | [📖](https://help.shapediver.com/doc/points-input) [🎥](https://www.youtube.com/watch?v=vXmEo7hJ5T0) | Points Input basic tutorial | [Model](https://www.shapediver.com/m/2i-pointsinputsimpleexample) / [App](https://appbuilder.shapediver.com/v1/main/latest/?slug=2i-pointsinputsimpleexample) | [Download](2I-PointsInputBasicTutorial.ghx) | ★ ★ ☆ | -| **2J** | [📖](https://help.shapediver.com/doc/points-input) [🎥](https://www.youtube.com/watch?v=h-8ro_QQ0CU) | Points Input with geometry constraints | [Model](https://www.shapediver.com/m/2j-pointsinputgeometryconstraints) / [App](https://appbuilder.shapediver.com/v1/main/latest/?slug=2j-pointsinputgeometryconstraints) | [Download](2J-PointsInputGeometryConstraints.ghx) | ★ ★ ☆ | -| **2K** | | Define various types of value lists with images | [Model](https://www.shapediver.com/app/m/2k-valuelistwithimages) / [App](https://www.shapediver.com/app/builder/v1/main/latest/?slug=2k-valuelistwithimages&redirect=0) | [Download](2K-ValueListWithImages.ghx) | ★ ★ ☆ | -| **2L** | | Create or override slider settings such as min and max values, step and marks. | [Model](https://www.shapediver.com/app/m/2l-slidersettings) / [App](https://www.shapediver.com/app/builder/v1/main/latest/?slug=2l-slidersettings&redirect=0) | [Download](2L-SliderSettings.ghx) | ★ ☆ ☆ | \ No newline at end of file +[inputs]: # diff --git a/src/02-Inputs/examples.json b/src/02-Inputs/examples.json new file mode 100644 index 0000000..d3174df --- /dev/null +++ b/src/02-Inputs/examples.json @@ -0,0 +1,148 @@ +{ + "2A-DirectTextInput.ghx": { + "anchor": "inputs", + "slug": "2a-directtextinput-27", + "title": "Direct Text Input", + "modelLink": true, + "appLink": false, + "rating": 1, + "docLink": "https://help.shapediver.com/doc/direct-text-input", + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "2B-FileImports.ghx": { + "anchor": "inputs", + "slug": "2b-fileimports", + "title": "File Imports", + "modelLink": true, + "appLink": false, + "rating": 1, + "docLink": "https://help.shapediver.com/doc/import-components", + "videoLink": "https://www.youtube.com/watch?v=6rZiZflGrRo", + "ghFiles": {}, + "files": {} + }, + "2C-ImportWithAttributes.ghx": { + "anchor": "inputs", + "slug": "2c-importwithattributes", + "title": "Import document properties and User Text using ShapeDiver Attributes.", + "modelLink": true, + "appLink": false, + "rating": 2, + "docLink": false, + "videoLink": false, + "ghFiles": { + "Input": "2C-ImportWithAttributes.3dm" + }, + "files": {} + }, + "2D-SelectionInput_Basic.ghx": { + "anchor": "inputs", + "slug": "2d-selectioninput-tutorial1", + "title": "Selection Input basic tutorial", + "modelLink": false, + "appLink": true, + "rating": 1, + "docLink": "https://help.shapediver.com/doc/selection-input", + "videoLink": "https://www.youtube.com/watch?v=m9BsPMvKPDo", + "ghFiles": {}, + "files": {} + }, + "2E-SelectionInput_Advanced.ghx": { + "anchor": "inputs", + "slug": "2e-selectioninput-tutorial2", + "title": "Use a Selection Input to influence both geometry and build a contextual App user interface", + "modelLink": false, + "appLink": true, + "rating": 2, + "docLink": "https://help.shapediver.com/doc/selection-input", + "videoLink": "https://www.youtube.com/watch?v=m9BsPMvKPDo", + "ghFiles": {}, + "files": {} + }, + "2F-GumballInput_Basic.ghx": { + "anchor": "inputs", + "slug": "2f-minimalgumballexample", + "title": "Gumball Input basic tutorial", + "modelLink": true, + "appLink": true, + "rating": 1, + "docLink": "https://help.shapediver.com/doc/gumball-input", + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "2G-GumballInput_Tutorial1.ghx": { + "anchor": "inputs", + "slug": "2g-gumballinput-tutorial1", + "title": "Use a Gumball Input to transform geometry in the viewer and apply the transformations in Grasshopper", + "modelLink": true, + "appLink": true, + "rating": 2, + "docLink": "https://help.shapediver.com/doc/gumball-input", + "videoLink": "https://www.youtube.com/watch?v=Pu9nDFJXZC0", + "ghFiles": {}, + "files": {} + }, + "2H-Gumballinput_Tutorial2.ghx": { + "anchor": "inputs", + "slug": "2h-gumballinput-tutorial2-3", + "title": "Advanced Gumball example with dynamic transformable objects.", + "modelLink": true, + "appLink": true, + "rating": 3, + "docLink": "https://help.shapediver.com/doc/gumball-input", + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "2I-PointsInputBasicTutorial.ghx": { + "anchor": "inputs", + "slug": "2i-pointsinputsimpleexample", + "title": "Points Input basic tutorial", + "modelLink": true, + "appLink": true, + "rating": 2, + "docLink": "https://help.shapediver.com/doc/points-input", + "videoLink": "https://www.youtube.com/watch?v=vXmEo7hJ5T0", + "ghFiles": {}, + "files": {} + }, + "2J-PointsInputGeometryConstraints.ghx": { + "anchor": "inputs", + "slug": "2j-pointsinputgeometryconstraints", + "title": "Points Input with geometry constraints", + "modelLink": true, + "appLink": true, + "rating": 2, + "docLink": "https://help.shapediver.com/doc/points-input", + "videoLink": "https://www.youtube.com/watch?v=h-8ro_QQ0CU", + "ghFiles": {}, + "files": {} + }, + "2K-ValueListWithImages.ghx": { + "anchor": "inputs", + "slug": "2k-valuelistwithimages", + "title": "Define various types of value lists with images", + "modelLink": true, + "appLink": true, + "rating": 2, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "2L-SliderSettings.ghx": { + "anchor": "inputs", + "slug": "2l-slidersettings", + "title": "Create or override slider settings such as min and max values, step and marks.", + "modelLink": true, + "appLink": true, + "rating": 1, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + } +} diff --git a/src/03-Outputs/definitions.md b/src/03-Outputs/definitions.md index 9acfc30..c65e876 100644 --- a/src/03-Outputs/definitions.md +++ b/src/03-Outputs/definitions.md @@ -1,9 +1,3 @@ # Outputs - -| Ref | Resources | Description | ShapeDiver links | Grasshopper definition | Level     | -|---------|---------|---------|---------|---------|---------| -| **3A** | [📖](https://help.shapediver.com/doc/download-export) | Export Components basic tutorial | [Model](https://www.shapediver.com/app/m/3a-exportexamples-27) | [Download](3A-ExportComponentsBasicTutorial.ghx) | ★ ☆ ☆ | -| **3B** | [📖](https://help.shapediver.com/doc/export-attributes) | Use attributes to export document properties (names, layers, colors, user text...). | [Model](https://www.shapediver.com/app/m/3b-exportwithattributes) | [Download](3B-ExportWithAttributes.ghx) | ★ ☆ ☆ | -| **3E** | [📖](https://help.shapediver.com/doc/direct-data-output) | Direct Data Output - Explore all supported data types. | -- | [Download](3E-DataOutputTypes.ghx) | ★ ☆ ☆ | -| **3H** | [📖](https://help.shapediver.com/doc/zip-streams) | Export multiple files using the Create Stream and Zip Streams components. | [Model](https://www.shapediver.com/app/m/3h-exportziparchive) | [Download](3H-ExportZipArchive.ghx) | ★ ☆ ☆ | \ No newline at end of file +[outputs]: # diff --git a/src/03-Outputs/examples.json b/src/03-Outputs/examples.json new file mode 100644 index 0000000..f6c4215 --- /dev/null +++ b/src/03-Outputs/examples.json @@ -0,0 +1,50 @@ +{ + "3A-ExportComponentsBasicTutorial.ghx": { + "anchor": "outputs", + "slug": "3a-exportexamples-27", + "title": "Export Components basic tutorial", + "modelLink": true, + "appLink": false, + "rating": 1, + "docLink": "https://help.shapediver.com/doc/download-export", + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "3B-ExportWithAttributes.ghx": { + "anchor": "outputs", + "slug": "3b-exportwithattributes", + "title": "Use attributes to export document properties (names, layers, colors, user text...).", + "modelLink": true, + "appLink": false, + "rating": 1, + "docLink": "https://help.shapediver.com/doc/export-attributes", + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "3E-DataOutputTypes.ghx": { + "anchor": "outputs", + "slug": "", + "title": "Direct Data Output - Explore all supported data types.", + "modelLink": false, + "appLink": false, + "rating": 1, + "docLink": "https://help.shapediver.com/doc/direct-data-output", + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "3H-ExportZipArchive.ghx": { + "anchor": "outputs", + "slug": "3h-exportziparchive", + "title": "Export multiple files using the Create Stream and Zip Streams components.", + "modelLink": true, + "appLink": false, + "rating": 1, + "docLink": "https://help.shapediver.com/doc/zip-streams", + "videoLink": false, + "ghFiles": {}, + "files": {} + } +} diff --git a/src/04-Display/definitions.md b/src/04-Display/definitions.md index 3765793..ccde013 100644 --- a/src/04-Display/definitions.md +++ b/src/04-Display/definitions.md @@ -1,8 +1,3 @@ # Display - -| Ref | Description | ShapeDiver links | Grasshopper definition | Level     | -|---------|---------|---------|---------|---------| -| **4A** | Display Basic tutorial - All the ways to display geometry in the ShapeDiver viewer | [Model](https://www.shapediver.com/app/m/4a-displaybasictutorial) | [Download](4A-DisplayBasicTutorial.ghx) | ★ ☆ ☆ | -| **4B** | Materials Presets | [Model](https://www.shapediver.com/app/m/4b-materialpresets) | [Download](4B-MaterialPresets.ghx) | ★ ☆ ☆ | -| **4C** | Rendering Guide: How to Make Your Models Look Great Online | [App](https://www.shapediver.com/app/builder/v1/main/latest/?slug=4c-renderingguide&redirect=0) | [START](4C-start.ghx) / [After Chapter 2](4C-after-chapter-2.ghx) / [After Chapter 3](4C-after-chapter-3.ghx) / [After Chapter 4](4C-after-chapter-4.ghx) / [FINAL](4C-RenderingGuide.ghx) | ★ ★ ☆ | \ No newline at end of file +[display]: # diff --git a/src/04-Display/examples.json b/src/04-Display/examples.json new file mode 100644 index 0000000..de33e5e --- /dev/null +++ b/src/04-Display/examples.json @@ -0,0 +1,44 @@ +{ + "4A-DisplayBasicTutorial.ghx": { + "anchor": "display", + "slug": "4a-displaybasictutorial", + "title": "Display Basic tutorial - All the ways to display geometry in the ShapeDiver viewer", + "modelLink": true, + "appLink": false, + "rating": 1, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "4B-MaterialPresets.ghx": { + "anchor": "display", + "slug": "4b-materialpresets", + "title": "Materials Presets", + "modelLink": true, + "appLink": false, + "rating": 1, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "4C-RenderingGuide.ghx": { + "anchor": "display", + "slug": "4c-renderingguide", + "title": "Rendering Guide: How to Make Your Models Look Great Online", + "modelLink": false, + "appLink": true, + "rating": 2, + "docLink": false, + "videoLink": false, + "ghFiles": { + "START": "4C-start.ghx", + "After Chapter 2": "4C-after-chapter-2.ghx", + "After Chapter 3": "4C-after-chapter-3.ghx", + "After Chapter 4": "4C-after-chapter-4.ghx", + "FINAL": "4C-RenderingGuide.ghx" + }, + "files": {} + } +} diff --git a/src/05-Images/definitions.md b/src/05-Images/definitions.md index f27e32e..1544e73 100644 --- a/src/05-Images/definitions.md +++ b/src/05-Images/definitions.md @@ -1,10 +1,3 @@ # Images - -| Ref | Description | ShapeDiver links | Grasshopper definition | Level     | -|---------|---------|---------|---------|---------| -| **5A** | Introduction to Grasshopper Bitmaps | [Model](https://www.shapediver.com/app/m/5a-grasshopperbitmap-8) | [Download](5A-GrasshopperBitmap.ghx) | ★ ☆ ☆ | -| **5B** | ShapeDiver Image Sampler | [Model](https://www.shapediver.com/app/m/5b-shapediverimagesampler-4) | [Download](5B-ShapeDiverImageSampler.ghx) | ★ ☆ ☆ | -| **5C** | ShapeDiver Image to Mesh | [Model](https://www.shapediver.com/app/m/5c-shapediverimagetomesh-40) | [Download](5C-ShapeDiverImageToMesh.ghx) | ★ ☆ ☆ | -| **5D** | Use the Squid plugin to generate bitmaps in Grasshopper | [Model](https://www.shapediver.com/app/m/5d-squidshapediveredition) | [Download](5D-SquidShapeDiverEdition.ghx) | ★ ★ ☆ | -| **5Dbis** | Use the Squid plugin to generate pdfs in Grasshopper | [Model](https://www.shapediver.com/app/m/5dbis-squidshapedivereditionpdf) | [Download](5Dbis-SquidShapeDiverEditionPDF.ghx) | ★ ★ ☆ | +[images]: # diff --git a/src/05-Images/examples.json b/src/05-Images/examples.json new file mode 100644 index 0000000..bbbf695 --- /dev/null +++ b/src/05-Images/examples.json @@ -0,0 +1,62 @@ +{ + "5A-GrasshopperBitmap.ghx": { + "anchor": "images", + "slug": "5a-grasshopperbitmap-8", + "title": "Introduction to Grasshopper Bitmaps", + "modelLink": true, + "appLink": false, + "rating": 1, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "5B-ShapeDiverImageSampler.ghx": { + "anchor": "images", + "slug": "5b-shapediverimagesampler-4", + "title": "ShapeDiver Image Sampler", + "modelLink": true, + "appLink": false, + "rating": 1, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "5C-ShapeDiverImageToMesh.ghx": { + "anchor": "images", + "slug": "5c-shapediverimagetomesh-40", + "title": "ShapeDiver Image to Mesh", + "modelLink": true, + "appLink": false, + "rating": 1, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "5D-SquidShapeDiverEdition.ghx": { + "anchor": "images", + "slug": "5d-squidshapediveredition", + "title": "Use the Squid plugin to generate bitmaps in Grasshopper", + "modelLink": true, + "appLink": false, + "rating": 2, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "5Dbis-SquidShapeDiverEditionPDF.ghx": { + "anchor": "images", + "slug": "5dbis-squidshapedivereditionpdf", + "title": "Use the Squid plugin to generate pdfs in Grasshopper", + "modelLink": true, + "appLink": false, + "rating": 2, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + } +} diff --git a/src/06-JSON/definitions.md b/src/06-JSON/definitions.md index 9273a11..56d24c2 100644 --- a/src/06-JSON/definitions.md +++ b/src/06-JSON/definitions.md @@ -1,6 +1,3 @@ # JSON - -| Ref | Description | ShapeDiver links | Grasshopper definition | Level     | -|---------|---------|---------|---------|---------| -| **6** | Introduction to JSON object manipulation | -- | [Download](6A-JSONTutorial.ghx) | ★ ☆ ☆ | \ No newline at end of file +[json]: # diff --git a/src/06-JSON/examples.json b/src/06-JSON/examples.json new file mode 100644 index 0000000..a13803f --- /dev/null +++ b/src/06-JSON/examples.json @@ -0,0 +1,14 @@ +{ + "6A-JSONTutorial.ghx": { + "anchor": "json", + "slug": "", + "title": "Introduction to JSON object manipulation", + "modelLink": false, + "appLink": false, + "rating": 1, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + } +} diff --git a/src/07-Attributes/definitions.md b/src/07-Attributes/definitions.md index 277845f..d4ea9c6 100644 --- a/src/07-Attributes/definitions.md +++ b/src/07-Attributes/definitions.md @@ -1,6 +1,3 @@ # Attributes - -| Ref | Resources | Description | ShapeDiver links | Grasshopper definition | Level     | -|---------|---------|---------|---------|---------|---------| -| **7A** | [📖](https://shapediver.atlassian.net/wiki/spaces/DOC/pages/2104721409/ShapeDiver+Output) | Basic attribute visualization setup. | [Model](https://www.shapediver.com/app/m/7a-attributevisualization-3) / [App](https://appbuilder.shapediver.com/v1/main/latest/?slug=7a-attributevisualization-3) | [Download](7A-AttributeVisualization.ghx) | ★ ☆ ☆ | \ No newline at end of file +[attributes]: # diff --git a/src/07-Attributes/examples.json b/src/07-Attributes/examples.json new file mode 100644 index 0000000..193415d --- /dev/null +++ b/src/07-Attributes/examples.json @@ -0,0 +1,14 @@ +{ + "7A-AttributeVisualization.ghx": { + "anchor": "attributes", + "slug": "7a-attributevisualization-3", + "title": "Basic attribute visualization setup.", + "modelLink": true, + "appLink": true, + "rating": 1, + "docLink": "https://shapediver.atlassian.net/wiki/spaces/DOC/pages/2104721409/ShapeDiver+Output", + "videoLink": false, + "ghFiles": {}, + "files": {} + } +} diff --git a/src/08-Plugins/definitions.md b/src/08-Plugins/definitions.md index 7e35284..6c06812 100644 --- a/src/08-Plugins/definitions.md +++ b/src/08-Plugins/definitions.md @@ -1,8 +1,3 @@ # Plugins & Integrations - -| Ref | Resources | Description | ShapeDiver links | Grasshopper definition | Level     | -|---------|---------|---------|---------|---------|---------| -| **8A** | [📖](https://help.shapediver.com/doc/shapediver-for-speckle) [🎥](https://www.youtube.com/watch?v=Son87CXLqkM) | Publish data to a Speckle model | [App](https://appbuilder.shapediver.com/v1/main/latest/?slug=8a-specklepublish-3) | [Download](8A-SpecklePublish.ghx) | ★ ☆ ☆ | -| **8B** | [📖](https://help.shapediver.com/doc/shapediver-for-speckle) [🎥](https://www.youtube.com/watch?v=Son87CXLqkM) | Load data from a Speckle model | [App](https://appbuilder.shapediver.com/v1/main/latest/?slug=8b-speckleload) | [Download](8B-SpeckleLoad.ghx) | ★ ☆ ☆ | -| **8C** | [📖](https://help.shapediver.com/doc/shapediver-for-skema) | Exchange data with a Skema project | [App](https://www.shapediver.com/app/builder/v1/main/latest/?slug=8c-skemaintegration&redirect=0) | [Download](8C-SkemaIntegration.ghx) | ★ ☆ ☆ | \ No newline at end of file +[plugins]: # diff --git a/src/08-Plugins/examples.json b/src/08-Plugins/examples.json new file mode 100644 index 0000000..329ed8e --- /dev/null +++ b/src/08-Plugins/examples.json @@ -0,0 +1,38 @@ +{ + "8A-SpecklePublish.ghx": { + "anchor": "plugins", + "slug": "8a-specklepublish-3", + "title": "Publish data to a Speckle model", + "modelLink": false, + "appLink": true, + "rating": 1, + "docLink": "https://help.shapediver.com/doc/shapediver-for-speckle", + "videoLink": "https://www.youtube.com/watch?v=Son87CXLqkM", + "ghFiles": {}, + "files": {} + }, + "8B-SpeckleLoad.ghx": { + "anchor": "plugins", + "slug": "8b-speckleload", + "title": "Load data from a Speckle model", + "modelLink": false, + "appLink": true, + "rating": 1, + "docLink": "https://help.shapediver.com/doc/shapediver-for-speckle", + "videoLink": "https://www.youtube.com/watch?v=Son87CXLqkM", + "ghFiles": {}, + "files": {} + }, + "8C-SkemaIntegration.ghx": { + "anchor": "plugins", + "slug": "8c-skemaintegration", + "title": "Exchange data with a Skema project", + "modelLink": false, + "appLink": true, + "rating": 1, + "docLink": "https://help.shapediver.com/doc/shapediver-for-skema", + "videoLink": false, + "ghFiles": {}, + "files": {} + } +} diff --git a/src/11-AppBuilder/definitions.md b/src/11-AppBuilder/definitions.md index 2d57de3..c1fb4ac 100644 --- a/src/11-AppBuilder/definitions.md +++ b/src/11-AppBuilder/definitions.md @@ -1,33 +1,17 @@ # App Builder -Some examples using [App Builder](https://help.shapediver.com/doc/shapediver-app-builder). +Some examples using [App Builder](https://help.shapediver.com/doc/shapediver-app-builder). ## Full workflows -| Ref | Description | ShapeDiver links | Grasshopper definition | Level     | -|---------|---------|---------|---------|---------| -| 11A | Build a simple user interface with controls and text widgets | [Model](https://www.shapediver.com/app/m/appbuilder-tutorial1-simpleexample) / [App](https://appbuilder.shapediver.com/v1/main/latest/?slug=appbuilder-tutorial1-simpleexample) | [Download](11A-AppBuilder_Tutorial1.ghx) | ★ ☆ ☆ | -| 11B | Use parametric logic to build a contextual interface | [Model](https://www.shapediver.com/app/m/appbuilder-tutorial2-contextualui) / [App](https://appbuilder.shapediver.com/v1/main/latest/?slug=appbuilder-tutorial2-contextualui) | [Download](11B-AppBuilder_Tutorial2.ghx) / [Input](11B-AppBuilder_Tutorial2_ExampleInput.3dm) | ★ ☆ ☆ | -| 11C | Link Export components to image widgets in your apps. | [Model](https://www.shapediver.com/app/m/appbuilder-tutorial3-imagewidget) / [App](https://appbuilder.shapediver.com/v1/main/latest/?slug=appbuilder-tutorial3-imagewidget) | [Download](11C-AppBuilder_Tutorial3.ghx) / [Input](11B-AppBuilder_Tutorial2_ExampleInput.3dm) | ★ ★ ☆ | -| 11D | Define charts and graphs | [Model](https://www.shapediver.com/app/m/appbuilder-tutorial4-charts) / [App](https://appbuilder.shapediver.com/v1/main/latest/?slug=appbuilder-tutorial4-charts) | [Download](11D-AppBuilder_Tutorial4.ghx) | ★ ★ ★ | +[workflows]: # + ## Components -| Ref | Description | ShapeDiver links | Grasshopper definition | Level     | -|---------|---------|---------|---------|---------| -| 11E | Parameter and Export Controls | [Model](https://www.shapediver.com/app/m/11e-parameterandexportcontrols) / [App](https://appbuilder.shapediver.com/v1/main/latest/?slug=11e-parameterandexportcontrols&redirect=0) | [Download](11E-ParameterAndExportControls.ghx) | ★ ☆ ☆ | -| 11F | Action Control | [Model](https://www.shapediver.com/app/m/11f-actioncontrol) / [App](https://appbuilder.shapediver.com/v1/main/latest/?slug=11f-actioncontrol) | [Download](11F-ActionControl.ghx) | ★ ☆ ☆ | -| 11G | Accordion and Stack Widgets | [Model](https://www.shapediver.com/app/m/11g-accordionandstackwidgets) / [App](https://appbuilder.shapediver.com/v1/main/latest/?slug=11g-accordionandstackwidgets) | [Download](11G-AccordionAndStackWidgets.ghx) | ★ ☆ ☆ | +[components]: # ## Interaction inputs in App Builder -| Ref | Resources | Description | ShapeDiver model | Grasshopper definition | Level     | -|---------|---------|---------|---------|---------|---------| -| **2D** | [📖](https://help.shapediver.com/doc/selection-input) [🎥](https://www.youtube.com/watch?v=m9BsPMvKPDo) | Selection Input basic tutorial | [Model]() / [App](https://appbuilder.shapediver.com/v1/main/latest/?slug=2d-selectioninput-tutorial1) | [Download](../02-Inputs/2D-SelectionInput_Basic.ghx) | ★ ☆ ☆ | -| **2E** | [📖](https://help.shapediver.com/doc/selection-input) [🎥](https://www.youtube.com/watch?v=m9BsPMvKPDo) | Use a Selection Input to influence both geometry and build a contextual App user interface | [Model]() / [App](https://appbuilder.shapediver.com/v1/main/latest/?slug=2e-selectioninput-tutorial2) | [Download](../02-Inputs/2E-SelectionInput_Advanced.ghx) | ★ ★ ☆ | -| **2F** | [📖](https://help.shapediver.com/doc/gumball-input) | Gumball Input basic tutorial | [Model](https://www.shapediver.com/app/m/2f-minimalgumballexample) / [App](https://appbuilder.shapediver.com/v1/main/latest/?slug=2f-minimalgumballexample) | [Download](../02-Inputs/2F-GumballInput_Basic.ghx) | ★ ☆ ☆ | -| **2G** | [📖](https://help.shapediver.com/doc/gumball-input) [🎥](https://www.youtube.com/watch?v=Pu9nDFJXZC0) | Use a Gumball Input to transform geometry in the viewer and apply the transformations in Grasshopper | [Model](https://www.shapediver.com/app/m/2g-gumballinput-tutorial1) / [App](https://appbuilder.shapediver.com/v1/main/latest/?slug=2g-gumballinput-tutorial1) | [Download](../02-Inputs/2G-GumballInput_Tutorial1.ghx) | ★ ★ ☆ | -| **2H** | [📖](https://help.shapediver.com/doc/gumball-input) | Advanced Gumball example with dynamic transformable objects. | [Model](https://www.shapediver.com/app/m/2h-gumballinput-tutorial2-3) / [App](https://www.shapediver.com/app/builder/v1/main/latest/?slug=2h-gumballinput-tutorial2-3&redirect=0) | [Download](../02-Inputs/2H-Gumballinput_Tutorial2.ghx) | ★ ★ ★ | -| **2I** | [📖](https://help.shapediver.com/doc/points-input) [🎥](https://www.youtube.com/watch?v=vXmEo7hJ5T0) | Points Input basic tutorial | [Model](https://www.shapediver.com/m/2i-pointsinputsimpleexample) / [App](https://appbuilder.shapediver.com/v1/main/latest/?slug=2i-pointsinputsimpleexample) | [Download](../02-Inputs/2I-PointsInputBasicTutorial.ghx) | ★ ★ ☆ | -| **2J** | [📖](https://help.shapediver.com/doc/points-input) [🎥](https://www.youtube.com/watch?v=h-8ro_QQ0CU) | Points Input with geometry constraints | [Model](https://www.shapediver.com/m/2j-pointsinputgeometryconstraints) / [App](https://appbuilder.shapediver.com/v1/main/latest/?slug=2j-pointsinputgeometryconstraints) | [Download](../02-Inputs/2J-PointsInputGeometryConstraints.ghx) | ★ ★ ☆ | \ No newline at end of file +[interaction]: # diff --git a/src/11-AppBuilder/examples.json b/src/11-AppBuilder/examples.json new file mode 100644 index 0000000..eea6206 --- /dev/null +++ b/src/11-AppBuilder/examples.json @@ -0,0 +1,174 @@ +{ + "11A-AppBuilder_Tutorial1.ghx": { + "anchor": "workflows", + "slug": "appbuilder-tutorial1-simpleexample", + "title": "Build a simple user interface with controls and text widgets", + "modelLink": true, + "appLink": true, + "rating": 1, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "11B-AppBuilder_Tutorial2.ghx": { + "anchor": "workflows", + "slug": "appbuilder-tutorial2-contextualui", + "title": "Use parametric logic to build a contextual interface", + "modelLink": true, + "appLink": true, + "rating": 1, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": { + "Input": "11B-AppBuilder_Tutorial2_ExampleInput.3dm" + } + }, + "11C-AppBuilder_Tutorial3.ghx": { + "anchor": "workflows", + "slug": "appbuilder-tutorial3-imagewidget", + "title": "Link Export components to image widgets in your apps.", + "modelLink": true, + "appLink": true, + "rating": 2, + "docLink": false, + "videoLink": false, + "ghFiles": { + "Input": "11B-AppBuilder_Tutorial2_ExampleInput.3dm" + }, + "files": {} + }, + "11D-AppBuilder_Tutorial4.ghx": { + "anchor": "workflows", + "slug": "appbuilder-tutorial4-charts", + "title": "Define charts and graphs", + "modelLink": true, + "appLink": true, + "rating": 3, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "11E-ParameterAndExportControls.ghx": { + "anchor": "components", + "slug": "11e-parameterandexportcontrols", + "title": "Parameter and Export Controls", + "modelLink": true, + "appLink": true, + "rating": 1, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "11F-ActionControl.ghx": { + "anchor": "components", + "slug": "11f-actioncontrol", + "title": "Action Control", + "modelLink": true, + "appLink": true, + "rating": 1, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "11G-AccordionAndStackWidgets.ghx": { + "anchor": "components", + "slug": "11g-accordionandstackwidgets", + "title": "Accordion and Stack Widgets", + "modelLink": true, + "appLink": true, + "rating": 1, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "../02-Inputs/2D-SelectionInput_Basic.ghx": { + "anchor": "interaction", + "slug": "2d-selectioninput-tutorial1", + "title": "Selection Input basic tutorial", + "modelLink": false, + "appLink": true, + "rating": 1, + "docLink": "https://help.shapediver.com/doc/selection-input", + "videoLink": "https://www.youtube.com/watch?v=m9BsPMvKPDo", + "ghFiles": {}, + "files": {} + }, + "../02-Inputs/2E-SelectionInput_Advanced.ghx": { + "anchor": "interaction", + "slug": "2e-selectioninput-tutorial2", + "title": "Use a Selection Input to influence both geometry and build a contextual App user interface", + "modelLink": false, + "appLink": true, + "rating": 2, + "docLink": "https://help.shapediver.com/doc/selection-input", + "videoLink": "https://www.youtube.com/watch?v=m9BsPMvKPDo", + "ghFiles": {}, + "files": {} + }, + "../02-Inputs/2F-GumballInput_Basic.ghx": { + "anchor": "interaction", + "slug": "2f-minimalgumballexample", + "title": "Gumball Input basic tutorial", + "modelLink": true, + "appLink": true, + "rating": 1, + "docLink": "https://help.shapediver.com/doc/gumball-input", + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "../02-Inputs/2G-GumballInput_Tutorial1.ghx": { + "anchor": "interaction", + "slug": "2g-gumballinput-tutorial1", + "title": "Use a Gumball Input to transform geometry in the viewer and apply the transformations in Grasshopper", + "modelLink": true, + "appLink": true, + "rating": 2, + "docLink": "https://help.shapediver.com/doc/gumball-input", + "videoLink": "https://www.youtube.com/watch?v=Pu9nDFJXZC0", + "ghFiles": {}, + "files": {} + }, + "../02-Inputs/2H-Gumballinput_Tutorial2.ghx": { + "anchor": "interaction", + "slug": "2h-gumballinput-tutorial2-3", + "title": "Advanced Gumball example with dynamic transformable objects.", + "modelLink": true, + "appLink": true, + "rating": 3, + "docLink": "https://help.shapediver.com/doc/gumball-input", + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "../02-Inputs/2I-PointsInputBasicTutorial.ghx": { + "anchor": "interaction", + "slug": "2i-pointsinputsimpleexample", + "title": "Points Input basic tutorial", + "modelLink": true, + "appLink": true, + "rating": 2, + "docLink": "https://help.shapediver.com/doc/points-input", + "videoLink": "https://www.youtube.com/watch?v=vXmEo7hJ5T0", + "ghFiles": {}, + "files": {} + }, + "../02-Inputs/2J-PointsInputGeometryConstraints.ghx": { + "anchor": "interaction", + "slug": "2j-pointsinputgeometryconstraints", + "title": "Points Input with geometry constraints", + "modelLink": true, + "appLink": true, + "rating": 2, + "docLink": "https://help.shapediver.com/doc/points-input", + "videoLink": "https://www.youtube.com/watch?v=h-8ro_QQ0CU", + "ghFiles": {}, + "files": {} + } +} diff --git a/src/12-DesktopClients/definitions.md b/src/12-DesktopClients/definitions.md index ee47734..fa68ef7 100644 --- a/src/12-DesktopClients/definitions.md +++ b/src/12-DesktopClients/definitions.md @@ -2,6 +2,4 @@ ShapeDiver Apps can interact with so-called "Desktop Clients", allowing to send and receive geometry from local software environments in real-time. So far, Rhino and Adobe Illustrator are supported as desktlop clients, as well as some custom integratiosn with industry-specific software. Get in touch if you would like us to support other desktop clients. -| Ref | Resources | Description | ShapeDiver links | Grasshopper definition | Level     | -|---------|---------|---------|---------|---------|---------| -| **2A** | _Soon_ | Introduction to Desktop Clients | [Model](https://www.shapediver.com/app/m/12a-desktopclientstutorial) / [App](https://appbuilder.shapediver.com/v1/main/latest/?slug=12a-desktopclientstutorial) | [Download](12A-DesktopClientsTutorial.ghx) | ★ ☆ ☆ | \ No newline at end of file +[clients]: # diff --git a/src/12-DesktopClients/examples.json b/src/12-DesktopClients/examples.json new file mode 100644 index 0000000..32ceae8 --- /dev/null +++ b/src/12-DesktopClients/examples.json @@ -0,0 +1,14 @@ +{ + "12A-DesktopClientsTutorial.ghx": { + "anchor": "clients", + "slug": "12a-desktopclientstutorial", + "title": "Introduction to Desktop Clients", + "modelLink": true, + "appLink": true, + "rating": 1, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + } +} diff --git a/src/BETA/definitions.md b/src/BETA/definitions.md index 77c7e8b..6e7a59b 100644 --- a/src/BETA/definitions.md +++ b/src/BETA/definitions.md @@ -6,18 +6,4 @@ Note that the final components and component flow might differ from the current > Some of the examples below make use of the JSwan Grasshopper plugin (version 1.2.0). -| Ref | Description | ShapeDiver links | Grasshopper definition | Level     | -|---------|---------|---------|---------|---------| -| BETA | Dynamic Sliders Tutorial | [Model](https://www.shapediver.com/app/m/beta-dynamicsliderstutorial) / [App](https://www.shapediver.com/app/builder/v1/main/latest/?slug=beta-dynamicsliderstutorial&redirect=0) | [Download](BETA-DynamicSlidersTutorial.ghx) | ★ ★ ☆ | -| BETA | Dynamic Value Lists Tutorial | [Model](https://www.shapediver.com/app/m/beta-dynamicvalueliststutorial) / [App](https://www.shapediver.com/app/builder/v1/main/latest/?slug=beta-dynamicvalueliststutorial&redirect=0) | [Download](BETA-DynamicValueListsTutorial.ghx) | ★ ★ ☆ | -| BETA | Define searchable value lists | [Model](https://www.shapediver.com/app/m/beta-searchablevaluelist-1) / [App](https://www.shapediver.com/app/builder/v1/main/latest/?slug=beta-searchablevaluelist-1&redirect=0) | [Download](BETA-SearchableValueList.ghx) | ★ ☆ ☆ | -| BETA | Define multiline text inputs | [Model](https://www.shapediver.com/app/m/beta-multilinetextinput) / [App](https://www.shapediver.com/app/builder/v1/main/latest/?slug=beta-multilinetextinput&redirect=0) | [Download](BETA-MultilineTextInput.ghx) | ★ ☆ ☆ | -| BETA | Control instances of other ShapeDiver models | [App](https://www.shapediver.com/app/builder/v1/main/latest/?slug=beta-instancestutorial1-controller&redirect=0) | [Download](BETA-InstancesTutorial1-Controller.ghx) / [Parametric Box](BETA-ParametricBoxInstance.ghx) | ★ ★ ☆ | -| BETA | Create anchor containers attached to 3d locations in the scene | [Model](https://www.shapediver.com/app/m/beta-anchor3d) / [App](https://www.shapediver.com/app/builder/v1/main/latest/?slug=beta-anchor3d&redirect=0) | [Download](BETA-Anchor3dSimple.ghx) | ★ ★ ☆ | -| BETA | Create anchor containers attached to 2d locations on the screen | [Model](https://www.shapediver.com/app/m/beta-anchor2d) / [App](https://www.shapediver.com/app/builder/v1/main/latest/?slug=beta-anchor2d&redirect=0) | [Download](BETA-Anchor2dTutorial.ghx) | ★ ★ ☆ | -| BETA | Play a sound| [Model](https://www.shapediver.com/app/m/beta-soundaction) / [App](https://www.shapediver.com/app/builder/v1/main/latest/?slug=beta-soundaction&redirect=0) | [Download](BETA-SoundAction.ghx) | ★ ☆ ☆ | -| BETA | Loop the value of a model state back to Grasshopper and include it in an export email | [Model](https://www.shapediver.com/app/m/beta-exportwithstate) / [App](https://www.shapediver.com/app/builder/v1/main/latest/?slug=beta-exportwithstate&redirect=0) | [Download](BETA-ExportWithState.ghx) | ★ ☆ ☆ | -| BETA | Send a screenshot of the App back to Grasshopper and include it in an export email | [Model](https://www.shapediver.com/app/m/beta-exportwithscreenshot) / [App](https://www.shapediver.com/app/builder/v1/main/latest/?slug=beta-exportwithscreenshot&redirect=0) | [Download](BETA-ExportWithScreenshot.ghx) | ★ ☆ ☆ | -| BETA | Create form inputs linked to a file export | [Model](https://www.shapediver.com/app/m/beta-formwidget) / [App](https://www.shapediver.com/app/builder/v1/main/latest/?slug=beta-formwidget&redirect=0) | [Download](BETA-FormWidget.ghx) | ★ ☆ ☆ | -| BETA | Trigger camera movements and animations using a Camera Action. | [Model](https://www.shapediver.com/app/m/beta-cameraaction) / [App](https://www.shapediver.com/app/builder/v1/main/latest/?slug=beta-cameraaction&redirect=0) | [Download](BETA-CameraAction.ghx) | ★ ★ ☆ | -| BETA | Saved States Widget | [Model](https://www.shapediver.com/app/m/beta-savedstateswidget) / [App](https://www.shapediver.com/app/builder/v1/main/latest/?slug=beta-savedstateswidget&redirect=0) | [Download](BETA-SavedStatesWidget.ghx) | ★ ☆ ☆ | \ No newline at end of file +[beta]: # diff --git a/src/BETA/examples.json b/src/BETA/examples.json new file mode 100644 index 0000000..a6c5449 --- /dev/null +++ b/src/BETA/examples.json @@ -0,0 +1,160 @@ +{ + "BETA-DynamicSlidersTutorial.ghx": { + "anchor": "beta", + "slug": "beta-dynamicsliderstutorial", + "title": "Dynamic Sliders Tutorial", + "modelLink": true, + "appLink": true, + "rating": 2, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "BETA-DynamicValueListsTutorial.ghx": { + "anchor": "beta", + "slug": "beta-dynamicvalueliststutorial", + "title": "Dynamic Value Lists Tutorial", + "modelLink": true, + "appLink": true, + "rating": 2, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "BETA-SearchableValueList.ghx": { + "anchor": "beta", + "slug": "beta-searchablevaluelist-1", + "title": "Define searchable value lists", + "modelLink": true, + "appLink": true, + "rating": 1, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "BETA-MultilineTextInput.ghx": { + "anchor": "beta", + "slug": "beta-multilinetextinput", + "title": "Define multiline text inputs", + "modelLink": true, + "appLink": true, + "rating": 1, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "BETA-InstancesTutorial1-Controller.ghx": { + "anchor": "beta", + "slug": "beta-instancestutorial1-controller", + "title": "Control instances of other ShapeDiver models", + "modelLink": false, + "appLink": true, + "rating": 2, + "docLink": false, + "videoLink": false, + "ghFiles": { + "Parametric Box": "BETA-ParametricBoxInstance.ghx" + }, + "files": {} + }, + "BETA-Anchor3dSimple.ghx": { + "anchor": "beta", + "slug": "beta-anchor3d", + "title": "Create anchor containers attached to 3d locations in the scene", + "modelLink": true, + "appLink": true, + "rating": 2, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "BETA-Anchor2dTutorial.ghx": { + "anchor": "beta", + "slug": "beta-anchor2d", + "title": "Create anchor containers attached to 2d locations on the screen", + "modelLink": true, + "appLink": true, + "rating": 2, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "BETA-SoundAction.ghx": { + "anchor": "beta", + "slug": "beta-soundaction", + "title": "Play a sound", + "modelLink": true, + "appLink": true, + "rating": 1, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "BETA-ExportWithState.ghx": { + "anchor": "beta", + "slug": "beta-exportwithstate", + "title": "Loop the value of a model state back to Grasshopper and include it in an export email", + "modelLink": true, + "appLink": true, + "rating": 1, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "BETA-ExportWithScreenshot.ghx": { + "anchor": "beta", + "slug": "beta-exportwithscreenshot", + "title": "Send a screenshot of the App back to Grasshopper and include it in an export email", + "modelLink": true, + "appLink": true, + "rating": 1, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "BETA-FormWidget.ghx": { + "anchor": "beta", + "slug": "beta-formwidget", + "title": "Create form inputs linked to a file export", + "modelLink": true, + "appLink": true, + "rating": 1, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "BETA-CameraAction.ghx": { + "anchor": "beta", + "slug": "beta-cameraaction", + "title": "Trigger camera movements and animations using a Camera Action.", + "modelLink": true, + "appLink": true, + "rating": 2, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "BETA-SavedStatesWidget.ghx": { + "anchor": "beta", + "slug": "beta-savedstateswidget", + "title": "Saved States Widget", + "modelLink": true, + "appLink": true, + "rating": 1, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + } +} diff --git a/src/Webinars/Webinar_2410_A_BarcelonaNeighbourhood.ghx b/src/Webinars/2410A-Webinar_BarcelonaNeighbourhood.ghx similarity index 100% rename from src/Webinars/Webinar_2410_A_BarcelonaNeighbourhood.ghx rename to src/Webinars/2410A-Webinar_BarcelonaNeighbourhood.ghx diff --git a/src/Webinars/Webinar_2410_B_ModularCabinets.ghx b/src/Webinars/2410B-Webinar_ModularCabinets.ghx similarity index 100% rename from src/Webinars/Webinar_2410_B_ModularCabinets.ghx rename to src/Webinars/2410B-Webinar_ModularCabinets.ghx diff --git a/src/Webinars/Webinar_2503_A_SpecklePavillion.ghx b/src/Webinars/2503A-Webinar_SpecklePavillion.ghx similarity index 100% rename from src/Webinars/Webinar_2503_A_SpecklePavillion.ghx rename to src/Webinars/2503A-Webinar_SpecklePavillion.ghx diff --git a/src/Webinars/Webinar_2503_B_ShopifyWorkflow.ghx b/src/Webinars/2503B-Webinar_ShopifyWorkflow.ghx similarity index 100% rename from src/Webinars/Webinar_2503_B_ShopifyWorkflow.ghx rename to src/Webinars/2503B-Webinar_ShopifyWorkflow.ghx diff --git a/src/Webinars/Webinar_2605_NewAppBuilderFeatures.ghx b/src/Webinars/2605-Webinar_NewAppBuilderFeatures.ghx similarity index 100% rename from src/Webinars/Webinar_2605_NewAppBuilderFeatures.ghx rename to src/Webinars/2605-Webinar_NewAppBuilderFeatures.ghx diff --git a/src/Webinars/definitions.md b/src/Webinars/definitions.md index 74dcf9a..375ebf3 100644 --- a/src/Webinars/definitions.md +++ b/src/Webinars/definitions.md @@ -1,13 +1,3 @@ # Webinar examples - -| Tutorial | Description | ShapeDiver model | Grasshopper definition | Level     | -|---------|---------|---------|---------|---------| -| **2410A** | [🎥 Fall '24 Update](https://www.youtube.com/watch?v=Rnu86ss0qT4): Barcelona Neighbourhood Analysis (Interactions components and Dynamic Parameters) | [Model](https://www.shapediver.com/app/m/webinar-2410-a-barcelonaneighbourhood) / [App](https://appbuilder.shapediver.com/v1/main/latest/?slug=webinar-2410-a-barcelonaneighbourhood) | [Download](Webinar_2410_A_BarcelonaNeighbourhood.ghx) | ★ ★ ☆ | -| **2410B** | [🎥 Fall '24 Update](https://www.youtube.com/watch?v=Rnu86ss0qT4): Modular cabinets configurator (Interactions components and Dynamic Parameters) | [Model](https://www.shapediver.com/app/m/webinar-2410-b-modularcabinets8) / [App](https://appbuilder.shapediver.com/v1/main/latest/?slug=webinar-2410-b-modularcabinets) | [Download](Webinar_2410_B_ModularCabinets.ghx) | ★ ★ ★ | -| **2503A** | [🎥 Spring '25 Update](https://shapediver.com/blog/shapediver-spring-25-update-live-webinar): Speckle pavillion | [Model](https://www.shapediver.com/app/m/2503a-speckle-pavillion) / [App](https://www.shapediver.com/app/builder/v1/main/latest/?slug=2503a-speckle-pavillion&redirect=0) | [Download](Webinar_2503_A_SpecklePavillion.ghx) | ★ ★ ☆ | -| **2503B** | [🎥 Spring '25 Update](https://www.youtube.com/watch?v=mAy7dLjXbGA): Shopify workflow | [Model](https://www.shapediver.com/app/m/2503b-shopifyworkflow) / [App](https://appbuilder.shapediver.com/v1/ijewel3d/latest/?slug=2503b-shopifyworkflow&g=https://cdn.shopify.com/s/files/1/0897/7802/8923/files/example-agent-ring-theme.json&webgiScene=https://cdn.shopify.com/s/files/1/0585/2721/9918/files/3dConfiguratorScene.vjson) | [Download](Webinar_2503_B_ShopifyWorkflow.ghx) | ★ ★ ☆ | -| **2605** | [🎥 Food4Rhino '26 Update](https://www.youtube.com/watch?v=A5qYzP4X_W4) | [App](https://www.shapediver.com/app/builder/v1/main/latest/?slug=webinar-2605-newappbuilderfeatures&redirect=0) | [Download](Webinar_2605_NewAppBuilderFeatures) | ★ ★ ☆ | - - - +[webinar]: # diff --git a/src/Webinars/examples.json b/src/Webinars/examples.json new file mode 100644 index 0000000..634e18b --- /dev/null +++ b/src/Webinars/examples.json @@ -0,0 +1,62 @@ +{ + "2410A-Webinar_BarcelonaNeighbourhood.ghx": { + "anchor": "webinar", + "slug": "webinar-2410-a-barcelonaneighbourhood", + "title": "[🎥 Fall '24 Update](https://www.youtube.com/watch?v=Rnu86ss0qT4): Barcelona Neighborhood Analysis (Interactions components and Dynamic Parameters)", + "modelLink": true, + "appLink": true, + "rating": 2, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "2410B-Webinar_ModularCabinets.ghx": { + "anchor": "webinar", + "slug": "webinar-2410-b-modularcabinets", + "title": "[🎥 Fall '24 Update](https://www.youtube.com/watch?v=Rnu86ss0qT4): Modular cabinets configurator (Interactions components and Dynamic Parameters)", + "modelLink": true, + "appLink": true, + "rating": 3, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "2503A-Webinar_SpecklePavillion.ghx": { + "anchor": "webinar", + "slug": "2503a-speckle-pavillion", + "title": "[🎥 Spring '25 Update](https://shapediver.com/blog/shapediver-spring-25-update-live-webinar): Speckle pavillion", + "modelLink": true, + "appLink": true, + "rating": 2, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "2503B-Webinar_ShopifyWorkflow.ghx": { + "anchor": "webinar", + "slug": "2503b-shopifyworkflow", + "title": "[🎥 Spring '25 Update](https://www.youtube.com/watch?v=mAy7dLjXbGA): Shopify workflow", + "modelLink": true, + "appLink": "https://appbuilder.shapediver.com/v1/ijewel3d/latest/?slug=2503b-shopifyworkflow&g=https://cdn.shopify.com/s/files/1/0897/7802/8923/files/example-agent-ring-theme.json&webgiScene=https://cdn.shopify.com/s/files/1/0585/2721/9918/files/3dConfiguratorScene.vjson", + "rating": 2, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + }, + "2605-Webinar_NewAppBuilderFeatures.ghx": { + "anchor": "webinar", + "slug": "webinar-2605-newappbuilderfeatures", + "title": "[🎥 Food4Rhino '26 Update](https://www.youtube.com/watch?v=A5qYzP4X_W4)", + "modelLink": false, + "appLink": true, + "rating": 2, + "docLink": false, + "videoLink": false, + "ghFiles": {}, + "files": {} + } +} diff --git a/tools/docs-cli/package.json b/tools/docs-cli/package.json new file mode 100644 index 0000000..608e19b --- /dev/null +++ b/tools/docs-cli/package.json @@ -0,0 +1,17 @@ +{ + "name": "@shapediver/docs-cli", + "version": "1.0.0", + "private": true, + "scripts": { + "create-tables": "tsx src/createTables.ts", + "validate": "tsx src/validate.ts" + }, + "dependencies": { + "zod": "4.3.6" + }, + "devDependencies": { + "@types/node": "24.1.0", + "tsx": "4.21.0", + "typescript": "6.0.2" + } +} diff --git a/tools/docs-cli/src/createTables.ts b/tools/docs-cli/src/createTables.ts new file mode 100644 index 0000000..1d86ed1 --- /dev/null +++ b/tools/docs-cli/src/createTables.ts @@ -0,0 +1,149 @@ +import fs from 'fs'; +import path from 'path'; +import { + Examples, + FILE_NAME_DEFINITIONS, + FILE_NAME_EXAMPLES, + MDBOOK_ANCHOR_REGEX, + SCHEMA_EXAMPLES, +} from './utils'; + +(function () { + const mdbookPath = process.argv[2]; + if (!fs.existsSync(mdbookPath)) { + console.error(`Path to mdbook does not exist: ${mdbookPath}`); + process.exit(1); + } + + // Find all folders one level deep in mdbook directory that contain an example file + const foldersWithExampleFile = fs + .readdirSync(mdbookPath, { withFileTypes: true }) + .filter((entry) => entry.isDirectory()) + .filter((entry) => fs.existsSync(path.join(mdbookPath, entry.name, FILE_NAME_EXAMPLES))); + + if (foldersWithExampleFile.length === 0) { + console.warn('No example files found in mdbook directory - nothing to do.'); + process.exit(0); + } + + for (const folderWithExampleFile of foldersWithExampleFile) { + const dir = path.join(mdbookPath, folderWithExampleFile.name), + tables: { [anchor: string]: Examples[] } = {}; + + console.log('Processing folder:', dir); + + try { + // Parse and validate examples JSON file + const examplesFile = path.join(dir, FILE_NAME_EXAMPLES), + fileContent = fs.readFileSync(examplesFile, 'utf-8'), + jsonData = JSON.parse(fileContent), + examplesData = SCHEMA_EXAMPLES.parse(jsonData); + + // Parse description markdown file + const markdownFile = path.join(dir, FILE_NAME_DEFINITIONS), + markdownData = fs.readFileSync(markdownFile, 'utf-8'); + + // Group examples by their anchor property + for (const [ghxName, example] of Object.entries(examplesData)) { + const { anchor } = example; + if (!tables[anchor]) tables[anchor] = []; + tables[anchor].push({ [ghxName]: example }); + } + + // Validate anchor count matches + const tableEntries = Object.entries(tables); + const matches = [...markdownData.matchAll(MDBOOK_ANCHOR_REGEX)]; + if (matches.length !== tableEntries.length) { + throw new Error(`Number of anchors in markdown file (${matches.length}) does not +match number of unique anchors in examples JSON (${tableEntries.length}).`); + } + + // Replace each anchor line with its generated table + const updatedMarkdown = markdownData.replace(MDBOOK_ANCHOR_REGEX, (_, anchor) => + createTable(tables[anchor] ?? []) + ); + + fs.writeFileSync(path.join(dir, FILE_NAME_DEFINITIONS), updatedMarkdown, 'utf-8'); + } catch (e) { + console.error('ERROR:', e); + process.exit(1); + } + } +})(); + +/** Converts a numeric rating (1-3) into a string of star characters. */ +function ratingToStars(rating: number): string { + return Array.from({ length: 3 }, (_, i) => (i < rating ? '★' : '☆')).join(' '); +} + +/** Creates an optional markdown link for boolean-or-string example metadata fields. */ +function createOptionalLink(icon: string, value: false | string): string { + return value ? `[${icon}](${value})` : ''; +} + +/** + * Creates a markdown table from the given examples, including conditional resources column if any + * example has links. + * @param examples The examples to include in the table, grouped by their anchor. + */ +function createTable(examples: Examples[]): string { + const hasResources = examples.some((example) => + Object.values(example).some((e) => e.docLink || e.videoLink) + ); + const hasFiles = examples.some((example) => + Object.values(example).some((e) => e.files && Object.keys(e.files).length > 0) + ); + + const columns = [ + 'Ref', + ...(hasResources ? ['Resources'] : []), + 'Description', + 'ShapeDiver links', + 'Grasshopper definition', + ...(hasFiles ? ['Files'] : []), + 'Level    ', + ]; + + const header = `| ${columns.join(' | ')} |\n| ${columns.map(() => '---').join(' | ')} |\n`; + + const rows = examples.map((example) => { + const [[ghxName, data]] = Object.entries(example); + const ref = `**${ghxName.split('/').at(-1)!.split('-')[0]}**`; + const resourceCell = hasResources + ? [createOptionalLink('📖', data.docLink), createOptionalLink('🎥', data.videoLink)] + .filter(Boolean) + .join(' ') + : null; + const shapeDiverLinks = [ + data.modelLink ? `[Model](https://www.shapediver.com/app/m/${data.slug})` : '', + typeof data.appLink === 'string' + ? `[App](${data.appLink})` + : data.appLink + ? `[App](https://www.shapediver.com/app/builder/v1/main/latest/?slug=${data.slug}&redirect=0)` + : '', + ] + .filter(Boolean) + .join(' / '); + const ghDefinitionCell = [ + `[Download](${ghxName})`, + ...Object.entries(data.ghFiles).map(([name, path]) => `[${name}](${path})`), + ].join(' / '); + const filesCell = Object.entries(data.files) + .map(([name, path]) => `[${name}](${path})`) + .join(' / '); + + const cells = [ + ref, + ...(resourceCell !== null ? [resourceCell] : []), + data.title, + shapeDiverLinks, + ghDefinitionCell, + ...(hasFiles ? [filesCell] : []), + ratingToStars(data.rating), + ]; + + return `| ${cells.join(' | ')} |`; + }); + + return header + rows.join('\n') + '\n'; +} diff --git a/tools/docs-cli/src/utils.ts b/tools/docs-cli/src/utils.ts new file mode 100644 index 0000000..d86839b --- /dev/null +++ b/tools/docs-cli/src/utils.ts @@ -0,0 +1,102 @@ +/* This file contains utility functions and constants for the docs-cli tool. */ +import { z } from 'zod'; + +/** The name of the JSON file holding information about ShapeDiver models used as examples. */ +export const FILE_NAME_EXAMPLES = 'examples.json'; + +/** The name of the Markdown file holding definitions for ShapeDiver models used as examples. */ +export const FILE_NAME_DEFINITIONS = 'definitions.md'; + +/** + * Strict string for metadata fields that are treated as user-authored identifiers or file paths. + * Rejects empty strings, whitespace-only strings, and strings with leading/trailing whitespace. + */ +const SCHEMA_NON_EMPTY_TRIMMED_STRING = z + .string() + .min(1) + .refine((value) => value.trim().length > 0, { + message: 'String must not be empty or whitespace only.', + }) + .refine((value) => value === value.trim(), { + message: 'String must not have leading or trailing whitespace.', + }); + +/** Optional documentation/video link fields are either disabled (`false`) or set explicitly. */ +const SCHEMA_OPTIONAL_LINK = z.union([z.literal(false), SCHEMA_NON_EMPTY_TRIMMED_STRING]); + +/** + * Record schema that validates keys with detailed string errors instead of Zod's generic + * "Invalid key in record" message. + */ +function createExamplesRecordSchema(valueSchema: T) { + return z.record(z.string(), valueSchema).superRefine((record, ctx) => { + for (const key of Object.keys(record)) { + const parsedKey = SCHEMA_NON_EMPTY_TRIMMED_STRING.safeParse(key); + if (!parsedKey.success) { + for (const issue of parsedKey.error.issues) { + ctx.addIssue({ + ...issue, + path: [key, ...issue.path], + }); + } + } + } + }); +} + +/** The schema of the JSON file holding information about ShapeDiver models used as examples. */ +export const SCHEMA_EXAMPLES = createExamplesRecordSchema( + z.object({ + /** + * The name of the markdown comment that acts as a placeholder for a single table. + * Format: + * `[anchor]: #`, where "anchor" is the name of the markdown comment. + * The markdown comment must be unique across all example definitions. + */ + anchor: SCHEMA_NON_EMPTY_TRIMMED_STRING, + + /** The slug of the ShapeDiver model. */ + slug: z.string(), + + /** The title of the ShapeDiver model. */ + title: SCHEMA_NON_EMPTY_TRIMMED_STRING, + + /** + * Indicates whether the model link should be included. + * When `true`, the model link is generated from the slug. + */ + modelLink: z.boolean(), + + /** + * Indicates whether the ShapeDiver application link should be included. When `true`, the + * AppBuilder link is generated from the slug. Alternatively, a string value can be provided + * to directly specify the app link. + */ + appLink: z.union([z.boolean(), z.string().min(1)]), + + /** The difficulty rating of the example. */ + rating: z.number().min(1).max(3).int(), + + /** Optional ShapeDiver documentation link for the example. */ + docLink: SCHEMA_OPTIONAL_LINK, + + /** Optional video link for the example. */ + videoLink: SCHEMA_OPTIONAL_LINK, + + /** + * List of auxiliary Grasshopper files that are shown next to the model's GH file's download + * link. Each key is a file description and each value is a file name. + */ + ghFiles: createExamplesRecordSchema(SCHEMA_NON_EMPTY_TRIMMED_STRING), + + /** + * List of additional non-Grasshopper files, where each key is a file description and each + * value is a file name. + */ + files: createExamplesRecordSchema(SCHEMA_NON_EMPTY_TRIMMED_STRING), + }) +); +export type Examples = z.infer; + +/** A regular expression to match markdown anchors in the format of `[anchor]: #`. */ +export const MDBOOK_ANCHOR_REGEX = new RegExp(/^\[([^\]]+)\]: #$/, 'gm'); diff --git a/tools/docs-cli/src/validate.ts b/tools/docs-cli/src/validate.ts new file mode 100644 index 0000000..e9e6ee6 --- /dev/null +++ b/tools/docs-cli/src/validate.ts @@ -0,0 +1,95 @@ +import fs from 'fs'; +import path from 'path'; +import { z } from 'zod'; +import { FILE_NAME_EXAMPLES, SCHEMA_EXAMPLES } from './utils'; + +/** + * Validate a file reference declared inside an examples.json file. + * + * All references are resolved relative to the examples.json file, may use `..`, must not be + * absolute, must exist, must be regular files, and must not be symlinks. + */ +function validateReferencedFile( + reference: string, + fieldPath: string, + examplesFilePath: string +): void { + if (path.isAbsolute(reference)) { + throw new Error( + `Invalid absolute path in '${fieldPath}': '${reference}' in ${examplesFilePath}.` + ); + } + + const absolutePath = path.resolve(path.dirname(examplesFilePath), reference); + if (!fs.existsSync(absolutePath)) { + throw new Error( + `Missing referenced file in '${fieldPath}': '${reference}' in ${examplesFilePath}.` + ); + } + + const stats = fs.lstatSync(absolutePath); + if (stats.isSymbolicLink()) { + throw new Error( + `Referenced file must not be a symlink in '${fieldPath}': '${reference}' in ${examplesFilePath}.` + ); + } + + if (!stats.isFile()) { + throw new Error( + `Referenced path is not a regular file in '${fieldPath}': '${reference}' in ${examplesFilePath}.` + ); + } +} + +(function () { + // husky passes the absolute path of the mdbook directory as an argument + const mdbookPath = process.argv[2]; + if (!fs.existsSync(mdbookPath)) { + console.error(`Path to mdbook does not exist: ${mdbookPath}`); + process.exit(1); + } + + // Find all example files one level deep in mdbook directory + const exampleFiles = fs + .readdirSync(mdbookPath, { withFileTypes: true }) + .filter((entry) => entry.isDirectory()) + .map((entry) => path.join(mdbookPath, entry.name, FILE_NAME_EXAMPLES)) + .filter((filePath) => fs.existsSync(filePath)); + + for (const filePath of exampleFiles) { + try { + const fileContent = fs.readFileSync(filePath, 'utf-8'); + const jsonData = JSON.parse(fileContent); + + // Validate JSON structure + const parsedData = SCHEMA_EXAMPLES.parse(jsonData); + + // Validate all file references declared by each example entry. + for (const [main, example] of Object.entries(parsedData)) { + // The top-level key is the main Grasshopper file for the example. + validateReferencedFile(main, main, filePath); + + // Auxiliary Grasshopper assets are declared as label -> relative file path. + for (const [label, referencedFile] of Object.entries(example.ghFiles)) { + validateReferencedFile(referencedFile, `${main}.ghFiles.${label}`, filePath); + } + + // Additional non-Grasshopper assets are declared as label -> relative file path. + for (const [label, referencedFile] of Object.entries(example.files)) { + validateReferencedFile(referencedFile, `${main}.files.${label}`, filePath); + } + } + } catch (e) { + console.error(`ERROR: Validation failed for ${filePath}:`); + if (e instanceof z.ZodError) { + console.error(e.issues); + } else if (e instanceof Error) { + console.error(e.message); + } else { + console.error(e); + } + console.error('\nValidation failed. Please fix the errors above before committing.'); + process.exit(1); + } + } +})(); diff --git a/tools/docs-cli/tsconfig.json b/tools/docs-cli/tsconfig.json new file mode 100644 index 0000000..d568158 --- /dev/null +++ b/tools/docs-cli/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "lib": [ + "ES2024" + ], + "types": [ + "node" + ], + "module": "nodenext", + "target": "ES2024", + "moduleResolution": "nodenext", + "noEmit": true, + "strict": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true + }, + "include": [ + "./src/**/*.ts" + ] +}