├── .all-contributorsrc ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml ├── pull_request_template.md └── workflows │ └── test_and_release.yml ├── .gitignore ├── .npmrc ├── .releaserc ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── astro-cloudinary ├── .gitignore ├── helpers │ └── package.json ├── index.ts ├── loaders │ └── package.json ├── package.json ├── src │ ├── components │ │ ├── CldImage.astro │ │ ├── CldOgImage.astro │ │ ├── CldUploadWidget.astro │ │ ├── CldVideoPlayer.astro │ │ └── index.ts │ ├── constants │ │ ├── analytics.ts │ │ └── sizes.ts │ ├── env.d.ts │ ├── helpers │ │ ├── getCldImageUrl.ts │ │ ├── getCldOgImageUrl.ts │ │ ├── getCldVideoUrl.ts │ │ └── index.ts │ ├── lib │ │ ├── cloudinary.ts │ │ ├── loader.ts │ │ ├── resources.ts │ │ └── util.ts │ ├── loaders │ │ ├── assets-loader.ts │ │ └── index.ts │ └── types │ │ └── resources.ts ├── tsconfig.json └── tsup.config.ts ├── docs ├── .gitignore ├── README.md ├── astro.config.mjs ├── package.json ├── public │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon.png │ ├── browserconfig.xml │ ├── favicon-1024x1024.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── favicon.svg │ └── site.webmanifest ├── src │ ├── assets │ │ └── houston.webp │ ├── components │ │ ├── Button.astro │ │ ├── CodeBlock.astro │ │ ├── DemoAssetsLoaderEcommerce.astro │ │ ├── DemoAssetsLoaderSamplesBasic.astro │ │ ├── DemoAssetsLoaderSamplesDefault.astro │ │ ├── DemoAssetsLoaderSamplesLimit.astro │ │ ├── DemoAssetsLoaderSamplesSingle.astro │ │ ├── DemoImageEvents.astro │ │ ├── DemoUploadWidgetSigned.astro │ │ ├── DemoUploadWidgetUnsigned.astro │ │ ├── DemoVideoPlayerEvents.astro │ │ ├── ExamplesCldOgImage.astro │ │ ├── Head.astro │ │ ├── HeaderImage.astro │ │ ├── ImageGrid.astro │ │ ├── Null.astro │ │ └── Table.astro │ ├── content.config.ts │ ├── content │ │ └── docs │ │ │ ├── cldassetsloader │ │ │ ├── basic-usage.mdx │ │ │ ├── configuration.mdx │ │ │ └── examples.mdx │ │ │ ├── cldimage │ │ │ ├── basic-usage.mdx │ │ │ ├── configuration.mdx │ │ │ └── examples.mdx │ │ │ ├── cldogimage │ │ │ ├── basic-usage.mdx │ │ │ └── configuration.mdx │ │ │ ├── clduploadwidget │ │ │ ├── basic-usage.mdx │ │ │ ├── configuration.mdx │ │ │ ├── examples.mdx │ │ │ └── signed-uploads.mdx │ │ │ ├── cldvideoplayer │ │ │ ├── basic-usage.mdx │ │ │ ├── configuration.mdx │ │ │ └── examples.mdx │ │ │ ├── getcldimageurl │ │ │ ├── basic-usage.mdx │ │ │ ├── configuration.mdx │ │ │ └── examples.mdx │ │ │ ├── getcldogimageurl │ │ │ ├── basic-usage.mdx │ │ │ ├── configuration.mdx │ │ │ └── examples.mdx │ │ │ ├── getcldvideourl │ │ │ ├── basic-usage.mdx │ │ │ ├── configuration.mdx │ │ │ └── examples.mdx │ │ │ ├── guides │ │ │ ├── background-removal.mdx │ │ │ ├── delivering-remote-images.mdx │ │ │ ├── image-optimization.mdx │ │ │ ├── image-overlays.mdx │ │ │ ├── responsive-images.mdx │ │ │ ├── social-media-card.mdx │ │ │ ├── text-overlays.mdx │ │ │ └── uploading-images-and-videos.mdx │ │ │ ├── index.mdx │ │ │ ├── installation.mdx │ │ │ └── templates │ │ │ └── social-media-cards.mdx │ ├── env.d.ts │ ├── lib │ │ └── util.ts │ ├── pages │ │ └── api │ │ │ └── sign-cloudinary-params.ts │ └── styles │ │ └── tailwind.css ├── tailwind.config.mjs └── tsconfig.json ├── package.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml └── scripts ├── .env.example ├── .gitignore ├── assets.json ├── collections.json ├── package.json └── upload-assets.js /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "commitConvention": "angular", 8 | "contributorsPerLine": 7, 9 | "skipCi": true, 10 | "repoType": "github", 11 | "repoHost": "https://github.com", 12 | "projectName": "astro-cloudinary", 13 | "projectOwner": "cloudinary-community", 14 | "commitType": "docs", 15 | "contributors": [ 16 | { 17 | "login": "colbyfayock", 18 | "name": "Colby Fayock", 19 | "avatar_url": "https://avatars.githubusercontent.com/u/1045274?v=4", 20 | "profile": "https://colbyfayock.com/newsletter", 21 | "contributions": [ 22 | "code", 23 | "doc" 24 | ] 25 | }, 26 | { 27 | "login": "Andarist", 28 | "name": "Mateusz Burzyński", 29 | "avatar_url": "https://avatars.githubusercontent.com/u/9800850?v=4", 30 | "profile": "https://github.com/Andarist", 31 | "contributions": [ 32 | "code" 33 | ] 34 | }, 35 | { 36 | "login": "hkbertoson", 37 | "name": "Hunter Bertoson", 38 | "avatar_url": "https://avatars.githubusercontent.com/u/44106297?v=4", 39 | "profile": "http://hunterbertoson.tech", 40 | "contributions": [ 41 | "code" 42 | ] 43 | }, 44 | { 45 | "login": "apatel369", 46 | "name": "Arpan Patel ", 47 | "avatar_url": "https://avatars.githubusercontent.com/u/33442948?v=4", 48 | "profile": "https://github.com/apatel369", 49 | "contributions": [ 50 | "doc" 51 | ] 52 | }, 53 | { 54 | "login": "saai-syvendra", 55 | "name": "Saai Syvendra", 56 | "avatar_url": "https://avatars.githubusercontent.com/u/157691467?v=4", 57 | "profile": "https://github.com/saai-syvendra", 58 | "contributions": [ 59 | "doc" 60 | ] 61 | }, 62 | { 63 | "login": "RaghavMangla", 64 | "name": "Raghav Mangla", 65 | "avatar_url": "https://avatars.githubusercontent.com/u/97332401?v=4", 66 | "profile": "https://github.com/RaghavMangla", 67 | "contributions": [ 68 | "doc" 69 | ] 70 | }, 71 | { 72 | "login": "kcoderhtml", 73 | "name": "Kieran Klukas", 74 | "avatar_url": "https://avatars.githubusercontent.com/u/92754843?v=4", 75 | "profile": "http://dunkirk.sh", 76 | "contributions": [ 77 | "code" 78 | ] 79 | }, 80 | { 81 | "login": "smv1256", 82 | "name": "S. M. V.", 83 | "avatar_url": "https://avatars.githubusercontent.com/u/117322465?v=4", 84 | "profile": "https://github.com/smv1256", 85 | "contributions": [ 86 | "doc" 87 | ] 88 | }, 89 | { 90 | "login": "ooloth", 91 | "name": "Michael Uloth", 92 | "avatar_url": "https://avatars.githubusercontent.com/u/625364?v=4", 93 | "profile": "https://michaeluloth.com", 94 | "contributions": [ 95 | "code" 96 | ] 97 | }, 98 | { 99 | "login": "justinphilpott", 100 | "name": "Justin Philpott", 101 | "avatar_url": "https://avatars.githubusercontent.com/u/8027086?v=4", 102 | "profile": "https://github.com/justinphilpott", 103 | "contributions": [ 104 | "doc" 105 | ] 106 | } 107 | ] 108 | } 109 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '[Bug] ' 5 | labels: 'Type: Bug' 6 | --- 7 | 8 | # **Bug Report** 9 | 10 | ## **Describe the bug** 11 | 12 | 13 | 14 | ## **Is this a regression?** 15 | 16 | 17 | 18 | 19 | ## **Steps To Reproduce the error** 20 | 21 | 22 | 23 | 1. 24 | 2. 25 | 3. 26 | 4. 27 | 28 | ## **Expected behaviour** 29 | 30 | 31 | 32 | ## **CodeSandbox or Live Example of Bug** 33 | 34 | 35 | 36 | ## **Screenshot or Video Recording** 37 | 38 | 39 | 40 | 41 | ### **Your environment** 42 | 43 | 45 | 46 | - OS: 47 | - Node version: 48 | - Npm version: 49 | - Browser name and version: 50 | 51 | 52 | ## **Additional context** 53 | 54 | 55 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Feature Request" 3 | about: "Suggest an idea or possible new feature for this project." 4 | title: "[Feature] " 5 | labels: "Type: Feature" 6 | --- 7 | 8 | # **Feature Request** 9 | 10 | ## **Is your feature request related to a problem? Please describe.** 11 | 12 | 13 | 14 | ## **Describe the solution you'd like** 15 | 16 | 17 | 18 | 19 | ## **Describe alternatives you've considered** 20 | 21 | 22 | 23 | ## **Additional context** 24 | 25 | 26 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "npm" 9 | directory: "/astro-cloudinary" 10 | schedule: 11 | interval: "weekly" 12 | - package-ecosystem: "npm" 13 | directory: "/" 14 | schedule: 15 | interval: "daily" 16 | allow: 17 | - dependency-name: "@cloudinary-util/url-loader" 18 | - dependency-name: "@cloudinary-util/util" 19 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | 4 | 5 | ## Issue Ticket Number 6 | 7 | Fixes # 8 | 9 | 10 | 11 | 12 | ## Type of change 13 | 14 | 15 | 16 | - [ ] Bug fix (non-breaking change which fixes an issue) 17 | - [ ] New feature (non-breaking change which adds functionality) 18 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 19 | - [ ] Fix or improve the documentation 20 | - [ ] This change requires a documentation update 21 | 22 | 23 | # Checklist 24 | 25 | 26 | 27 | - [ ] I have followed the contributing guidelines of this project as mentioned in [CONTRIBUTING.md](/CONTRIBUTING.md) 28 | - [ ] I have created an [issue](https://github.com/cloudinary-community/astro-cloudinary/issues) ticket for this PR 29 | - [ ] I have checked to ensure there aren't other open [Pull Requests](https://github.com/cloudinary-community/astro-cloudinary/pulls) for the same update/change? 30 | - [ ] I have performed a self-review of my own code 31 | - [ ] I have run tests locally to ensure they all pass 32 | - [ ] I have commented my code, particularly in hard-to-understand areas 33 | - [ ] I have made corresponding changes needed to the documentation 34 | -------------------------------------------------------------------------------- /.github/workflows/test_and_release.yml: -------------------------------------------------------------------------------- 1 | name: Test & Release 2 | on: [push, pull_request] 3 | env: 4 | CI: false 5 | jobs: 6 | tests: 7 | name: Test 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | node: [ '18', '20' ] 12 | steps: 13 | - uses: actions/checkout@v2 14 | 15 | - uses: pnpm/action-setup@v2 16 | with: 17 | version: 9 18 | 19 | - uses: actions/setup-node@v3 20 | with: 21 | node-version: ${{ matrix.node }} 22 | cache: 'pnpm' 23 | 24 | - run: pnpm install --frozen-lockfile 25 | working-directory: ./astro-cloudinary 26 | 27 | # - run: pnpm test 28 | # working-directory: ./astro-cloudinary 29 | 30 | # - run: pnpm test:app 31 | # working-directory: ./astro-cloudinary 32 | 33 | release: 34 | name: Release 35 | if: ${{ github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'beta') }} 36 | needs: tests 37 | runs-on: ubuntu-latest 38 | steps: 39 | - uses: actions/checkout@v2 40 | with: 41 | fetch-depth: 0 42 | 43 | - uses: pnpm/action-setup@v2 44 | with: 45 | version: 9 46 | 47 | - uses: actions/setup-node@v3 48 | with: 49 | node-version: '20' 50 | cache: 'pnpm' 51 | # https://github.com/pnpm/pnpm/issues/3141 52 | registry-url: 'https://registry.npmjs.org' 53 | 54 | - run: pnpm install --frozen-lockfile 55 | 56 | - run: pnpm release 57 | env: 58 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 59 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 60 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 61 | 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vscode 3 | .env 4 | astro-cloudinary/dist 5 | .idea 6 | tsconfig.tsbuildinfo 7 | .vercel -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | auto-install-peers = true -------------------------------------------------------------------------------- /.releaserc: -------------------------------------------------------------------------------- 1 | { 2 | "branches": [ 3 | "main", 4 | "next", 5 | "next-major", 6 | { 7 | "name": "beta", 8 | "prerelease": true 9 | }, 10 | { 11 | "name": "alpha", 12 | "prerelease": true 13 | } 14 | ], 15 | "plugins": [ 16 | [ 17 | "@semantic-release/commit-analyzer", 18 | { 19 | "preset": "angular", 20 | "releaseRules": [ 21 | { 22 | "type": "docs", 23 | "scope": "README", 24 | "release": "patch" 25 | } 26 | ], 27 | "parserOpts": { 28 | "noteKeywords": [ 29 | "BREAKING CHANGE", 30 | "BREAKING CHANGES" 31 | ] 32 | } 33 | } 34 | ], 35 | "@semantic-release/release-notes-generator", 36 | [ 37 | "@semantic-release/changelog", 38 | { 39 | "changelogFile": "CHANGELOG.md" 40 | } 41 | ], 42 | [ 43 | "@colbyfayock/semantic-release-pnpm", 44 | { 45 | "pkgRoot": "astro-cloudinary", 46 | "publishBranch": "main|beta" 47 | } 48 | ], 49 | [ 50 | "@semantic-release/git", 51 | { 52 | "assets": [ 53 | "astro-cloudinary/package.json", 54 | "CHANGELOG.md" 55 | ] 56 | } 57 | ], 58 | "@semantic-release/github" 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | eric@cloudinary.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind are welcome! 3 | 4 | ## Issues 5 | 6 | ### Creating an Issue 7 | If you find a bug or problem, or maybe the documentation just doesn't make sense, please create an Issue to document the concern. 8 | 9 | ### Description 10 | Please be descriptive in your Issue. The more info you provide, the more likely someone will be able to help. 11 | 12 | ### Code Examples 13 | If you're experiencing an issue with the code, the most helpful thing you can do is create an example where you can reproduce the problem. This can be an open-source GitHub repo, a private repo you can share with the maintainers, a [CodeSandbox](https://codesandbox.io/), or anything to show the issue live with code alongside it. 14 | 15 | ## Pull Requests 16 | 17 | ### Creating a Pull Request 18 | If you're able to fix an active Issue, feel free to create a new Pull Request addressing the problem. There are no guarantees that the code will be merged in "as is", but chances are, if you're willing to work with the maintainers, everyone will be able to come up with a solution everyone can be happy with. 19 | 20 | ### Description 21 | Please be descriptive in your Pull Request. Whether big or small, it's important to be able to see the context of a change throughout the history of a project. 22 | 23 | ### Linking Fixed Issues 24 | If the Pull Request is addressing an Issue, please link that issue by specifying the `Fixes [Issue #]` syntax within the Pull Request. 25 | 26 | ### Getting Added to All Contributors in the README.md 27 | Once your Pull Request is successfully merged, feel free to tag yourself using the [All Contributors syntax](https://allcontributors.org/docs/en/bot/usage), which will create a new Pull Request requesting to add you in. 28 | 29 | ``` 30 | @all-contributors please add for 31 | ``` 32 | 33 | If your Pull Request is merged in and you're not added, please let someone know if you don't want to tag yourself, as we want to recognize everyone for their help. 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Cloudinary 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /astro-cloudinary/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /astro-cloudinary/helpers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "../dist/helpers/index.js" 3 | } -------------------------------------------------------------------------------- /astro-cloudinary/index.ts: -------------------------------------------------------------------------------- 1 | export * from './src/components/index.js'; 2 | // export * from './helpers.js'; 3 | 4 | // export { default as CldOgImage } from './components/CldOgImage'; 5 | // export type { CldOgImageProps } from './components/CldOgImage'; 6 | 7 | // export { default as CldUploadButton } from './components/CldUploadButton'; 8 | // export type { CldUploadButtonProps } from './components/CldUploadButton'; 9 | 10 | // export { default as CldUploadWidget } from './components/CldUploadWidget'; 11 | // export type { CldUploadWidgetProps, CldUploadWidgetPropsChildren } from './components/CldUploadWidget'; -------------------------------------------------------------------------------- /astro-cloudinary/loaders/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "../dist/loaders/index.js" 3 | } -------------------------------------------------------------------------------- /astro-cloudinary/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "astro-cloudinary", 3 | "version": "1.3.2", 4 | "type": "module", 5 | "license": "MIT", 6 | "module": "./index.ts", 7 | "keywords": [ 8 | "cloudinary", 9 | "astro", 10 | "astro-loader" 11 | ], 12 | "exports": { 13 | ".": { 14 | "import": "./index.ts" 15 | }, 16 | "./helpers": { 17 | "types": "./dist/helpers/index.d.ts", 18 | "import": "./dist/helpers/index.js" 19 | }, 20 | "./loaders": { 21 | "types": "./dist/loaders/index.d.ts", 22 | "import": "./dist/loaders/index.js" 23 | }, 24 | "./package.json": "./package.json" 25 | }, 26 | "files": [ 27 | "dist", 28 | "helpers", 29 | "loaders", 30 | "src", 31 | "index.ts" 32 | ], 33 | "scripts": { 34 | "dev": "tsup --watch", 35 | "build": "tsup", 36 | "prepublishOnly": "cp ../README.md . && cp ../LICENSE . && pnpm build", 37 | "postpublish": "rm ./README.md && rm ./LICENSE" 38 | }, 39 | "dependencies": { 40 | "@cloudinary-util/types": "1.6.0", 41 | "@cloudinary-util/url-loader": "6.0.0", 42 | "@cloudinary-util/util": "^4.1.0", 43 | "@cloudinary/url-gen": "^1.20.0", 44 | "@unpic/astro": "^0.0.47", 45 | "@unpic/core": "^0.0.49", 46 | "tsup": "^8.4.0", 47 | "unpic": "^3.18.0" 48 | }, 49 | "peerDependencies": { 50 | "astro": "^3.2.0 || ^4.0.0 || ^5.0.0" 51 | }, 52 | "devDependencies": { 53 | "astro": "^5.5.3" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /astro-cloudinary/src/components/CldImage.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { type HTMLAttributes } from 'astro/types'; 3 | import { type UnpicImageProps } from '@unpic/core'; 4 | import { Image } from '@unpic/astro'; 5 | import { cloudinaryPluginKeys, type ConfigOptions, type ImageOptions } from '@cloudinary-util/url-loader'; 6 | 7 | import { generateImageLoader } from '../lib/loader'; 8 | 9 | export type CldImageProps = Omit>, "src"> & ImageOptions & { 10 | config?: ConfigOptions; 11 | src: string; 12 | } 13 | 14 | interface Props extends CldImageProps {}; 15 | 16 | const { config, class: className, ...props }: Props = Astro.props; 17 | 18 | // Add props here that are intended to only be used for 19 | // Cloudinary URL construction to avoid them being forwarded 20 | // to the DOM. These are props that aren't automatically 21 | // pulled from the URL Loader plugins 22 | 23 | const CLOUDINARY_PROPS = [ 24 | 'assetType', 25 | 'deliveryType', 26 | 'dpr', 27 | 'format', 28 | 'quality', 29 | 'strictTransformations', 30 | ...cloudinaryPluginKeys 31 | 32 | // Excluded props that are managed by the component 33 | // 'height', 34 | // 'src', 35 | // 'width' 36 | ]; 37 | 38 | // Collect all of the props that are intended to be passed 39 | // into the URL construction 40 | 41 | const cldOptions: ImageOptions = { 42 | height: props.height, 43 | src: props.src, 44 | width: props.width, 45 | }; 46 | 47 | CLOUDINARY_PROPS.forEach((key) => { 48 | const prop = props[key as keyof ImageOptions]; 49 | if ( prop ) { 50 | // @ts-expect-error 51 | cldOptions[key as keyof ImageOptions] = prop; 52 | } 53 | }); 54 | 55 | // Create a new instance of the loader for the Image component to 56 | // vend URLs for each responsive breakpoint 57 | 58 | const transformer = generateImageLoader(cldOptions, config); 59 | 60 | // Construct the base Image component props by filtering out Cloudinary-specific props 61 | 62 | const src = transformer({ 63 | url: props.src, 64 | width: props.width, 65 | height: props.height 66 | }); 67 | 68 | const imageProps = { 69 | cdn: 'cloudinary', 70 | height: props.height, 71 | src, 72 | width: props.width, 73 | transformer, 74 | } as UnpicImageProps>; 75 | 76 | // Loop through all props and try to find any that are not valid 77 | // Cloudinary props to assume they are intended to be passed through 78 | // to the Image component 79 | 80 | Object.entries(props) 81 | .filter(([key]) => typeof key === 'string' && !CLOUDINARY_PROPS.includes(key)) 82 | .forEach(([key, value]) => imageProps[key as keyof UnpicImageProps>] = value); 83 | 84 | let imageClassName = 'astro-cloudinary-cldimage'; 85 | 86 | if (className) { 87 | imageClassName = `${imageClassName} ${className}`; 88 | } 89 | --- 90 | 91 | 92 | -------------------------------------------------------------------------------- /astro-cloudinary/src/components/CldOgImage.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import type { CldImageProps } from "./CldImage.astro"; 3 | import { getCldOgImageUrl } from "../helpers/getCldOgImageUrl"; 4 | import { OG_IMAGE_WIDTH, OG_IMAGE_HEIGHT } from "../constants/sizes"; 5 | 6 | export type CldOgImageProps = CldImageProps & { 7 | twitterTitle: string; 8 | }; 9 | 10 | interface Props extends CldOgImageProps {} 11 | 12 | const { twitterTitle, alt, ...props }: Props = Astro.props; 13 | 14 | // check if the twitter title exists 15 | if (!twitterTitle) { 16 | throw new Error("twitterTitle is required for the CldOgImage component"); 17 | } 18 | 19 | // We need to separately handle the width and the height to allow our user to pass in 20 | // a custom value, but also we need to know this at the component level so that we can 21 | // use it when rendering the meta tags 22 | let { width = OG_IMAGE_WIDTH, height = OG_IMAGE_HEIGHT } = props; 23 | 24 | // Normalize the width and height 25 | width = typeof width === "string" ? Number.parseInt(width) : width; 26 | height = typeof height === "string" ? Number.parseInt(height) : height; 27 | 28 | // Render the final URLs. We use two different format versions to deliver 29 | // webp for Twitter as it supports it (and we can control with tags) where 30 | // other platforms may not support webp, so we deliver jpg 31 | const ogImageUrl = getCldOgImageUrl({ 32 | ...Astro.props, 33 | width, 34 | height, 35 | }); 36 | 37 | const twitterImageUrl = getCldOgImageUrl({ 38 | ...Astro.props, 39 | width, 40 | height, 41 | format: Astro.props.format || "webp", 42 | }); 43 | --- 44 | 45 | 46 | 47 | 48 | 49 | {alt && } 50 | 51 | 52 | -------------------------------------------------------------------------------- /astro-cloudinary/src/components/CldUploadWidget.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { type CloudinaryUploadWidgetOptions } from '@cloudinary-util/types'; 3 | import { getUploadWidgetOptions, type ConfigOptions } from "@cloudinary-util/url-loader"; 4 | import { getCloudinaryConfig } from '../lib/cloudinary'; 5 | 6 | export interface CldUploadWidgetProps { 7 | class?: string; 8 | config?: ConfigOptions; 9 | id?: string; 10 | options?: CloudinaryUploadWidgetOptions; 11 | signatureEndpoint?: URL | RequestInfo; 12 | uploadPreset?: string; 13 | } 14 | 15 | interface Props extends CldUploadWidgetProps {}; 16 | 17 | const { class: className, config, id, options, signatureEndpoint, uploadPreset }: Props = Astro.props; 18 | 19 | // When creating a signed upload, you need to provide both your Cloudinary API Key 20 | // as well as a signature generator function that will sign any paramters 21 | // either on page load or during the upload process. Read more about signed uploads at: 22 | // https://cloudinary.com/documentation/upload_widget#signed_uploads 23 | 24 | const cloudinaryConfig = getCloudinaryConfig(config); 25 | 26 | const uploadOptions = getUploadWidgetOptions({ 27 | uploadPreset: uploadPreset || import.meta.env.PUBLIC_CLOUDINARY_UPLOAD_PRESET, 28 | ...options, 29 | }, cloudinaryConfig); 30 | 31 | let playerClassName = 'astro-cloudinary-clduploadwidget'; 32 | 33 | if ( typeof className === 'string' ) { 34 | playerClassName = `${playerClassName} ${className}`; 35 | } 36 | --- 37 | 38 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /astro-cloudinary/src/components/CldVideoPlayer.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { createHash } from "node:crypto"; 3 | import { getVideoPlayerOptions, type ConfigOptions } from '@cloudinary-util/url-loader'; 4 | 5 | import { getCloudinaryConfig } from '../lib/cloudinary'; 6 | import type { CloudinaryVideoPlayerOptions, CloudinaryVideoPlayerOptionsLogo } from '@cloudinary-util/types'; 7 | import type { GetCldImageUrlOptions } from "../helpers/getCldImageUrl"; 8 | import type { GetCldVideoUrlOptions } from "../helpers/getCldVideoUrl"; 9 | 10 | const PLAYER_VERSION = '2.1.0'; 11 | 12 | export interface CldVideoPlayerProps extends Omit { 13 | class?: string; 14 | config?: ConfigOptions; 15 | // Redefine to make it required 16 | height: CloudinaryVideoPlayerOptions["height"]; 17 | id?: string; 18 | logo?: boolean | CldVideoPlayerPropsLogo; 19 | poster?: string | GetCldImageUrlOptions | GetCldVideoUrlOptions; 20 | src: string; 21 | quality?: string | number; 22 | // Redefine to make it required 23 | width: CloudinaryVideoPlayerOptions["width"]; 24 | } 25 | 26 | export interface CldVideoPlayerPropsLogo { 27 | imageUrl?: CloudinaryVideoPlayerOptionsLogo['logoImageUrl']; 28 | logo?: boolean; 29 | onClickUrl?: CloudinaryVideoPlayerOptionsLogo['logoOnclickUrl']; 30 | } 31 | 32 | interface Props extends CldVideoPlayerProps {}; 33 | 34 | const props: Props = Astro.props; 35 | const { class: className, config, height, id, width } = props; 36 | 37 | const cloudinaryConfig = getCloudinaryConfig(config); 38 | const playerOptions = getVideoPlayerOptions(props, cloudinaryConfig); 39 | const { publicId } = playerOptions; 40 | 41 | if ( typeof publicId === 'undefined' ) { 42 | throw new Error('[CldVideoPlayer] Public ID or Cloudinary URL required - please specify a src prop.'); 43 | } 44 | 45 | // Each player needs a unique ID in order to be properly configured 46 | // in the event that there are multiple players on a single page. 47 | // Otherwise, if for instance one player has theme customization 48 | // and another doesn't, you may have unexpected results such as 49 | // all players having the same theme 50 | 51 | let playerId = id || `player-${publicId.replace('/', '-')}`; 52 | 53 | const playerHash = createHash('shake256', { outputLength: 4 }) 54 | .update(JSON.stringify(playerOptions)) 55 | .digest("hex"); 56 | 57 | let playerClassName = 'astro-cloudinary-cldvideoplayer cld-video-player cld-fluid'; 58 | 59 | if ( className ) { 60 | playerClassName = `${playerClassName} ${className}`; 61 | } 62 | --- 63 | 64 | 65 |
66 |
75 | 76 | -------------------------------------------------------------------------------- /astro-cloudinary/src/components/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | default as CldImage, 3 | type CldImageProps 4 | } from './CldImage.astro'; 5 | 6 | export { 7 | default as CldOgImage, 8 | type CldOgImageProps 9 | } from './CldOgImage.astro'; 10 | 11 | export { 12 | default as CldVideoPlayer, 13 | type CldVideoPlayerProps, 14 | type CldVideoPlayerPropsLogo 15 | } from './CldVideoPlayer.astro'; 16 | 17 | export { 18 | default as CldUploadWidget, 19 | type CldUploadWidgetProps, 20 | } from './CldUploadWidget.astro'; -------------------------------------------------------------------------------- /astro-cloudinary/src/constants/analytics.ts: -------------------------------------------------------------------------------- 1 | import astroPkg from 'astro/package.json'; 2 | import pkg from '../../package.json'; 3 | 4 | export const ASTRO_CLOUDINARY_ANALYTICS_PRODUCT_ID = 'B'; 5 | export const ASTRO_CLOUDINARY_ANALYTICS_ID = 'G'; 6 | export const ASTRO_VERSION = normalizeVersion(astroPkg.version); 7 | export const ASTRO_CLOUDINARY_VERSION = normalizeVersion(pkg.version); 8 | 9 | function normalizeVersion(version: string) { 10 | let normalized = version; 11 | if ( normalized.includes('-') ) { 12 | normalized = normalized.split('-')[0]; 13 | } 14 | return normalized; 15 | } -------------------------------------------------------------------------------- /astro-cloudinary/src/constants/sizes.ts: -------------------------------------------------------------------------------- 1 | export const OG_IMAGE_WIDTH = 1200; 2 | export const OG_IMAGE_HEIGHT = 630; -------------------------------------------------------------------------------- /astro-cloudinary/src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// -------------------------------------------------------------------------------- /astro-cloudinary/src/helpers/getCldImageUrl.ts: -------------------------------------------------------------------------------- 1 | import { constructCloudinaryUrl } from '@cloudinary-util/url-loader'; 2 | import type { ImageOptions, ConfigOptions, AnalyticsOptions } from '@cloudinary-util/url-loader'; 3 | 4 | import { getCloudinaryConfig, getCloudinaryAnalytics } from "../lib/cloudinary.js"; 5 | 6 | /** 7 | * getCldImageUrl 8 | */ 9 | 10 | export type GetCldImageUrlOptions = ImageOptions; 11 | export type GetCldImageUrlConfig = ConfigOptions; 12 | export type GetCldImageUrlAnalytics = AnalyticsOptions; 13 | 14 | export function getCldImageUrl(options: GetCldImageUrlOptions, config?: GetCldImageUrlConfig, analytics?: GetCldImageUrlAnalytics) { 15 | return constructCloudinaryUrl({ 16 | options, 17 | config: getCloudinaryConfig(config), 18 | analytics: getCloudinaryAnalytics(analytics) 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /astro-cloudinary/src/helpers/getCldOgImageUrl.ts: -------------------------------------------------------------------------------- 1 | import { OG_IMAGE_WIDTH, OG_IMAGE_HEIGHT } from '../constants/sizes.js'; 2 | 3 | import { getCldImageUrl } from './getCldImageUrl.js'; 4 | import type { GetCldImageUrlOptions, GetCldImageUrlConfig, GetCldImageUrlAnalytics } from './getCldImageUrl.js'; 5 | 6 | /** 7 | * getCldImageUrl 8 | */ 9 | 10 | export type GetCldOgImageUrlOptions = GetCldImageUrlOptions; 11 | export type GetCldOgImageUrlConfig = GetCldImageUrlConfig; 12 | export type GetCldOgImageUrlAnalytics = GetCldImageUrlAnalytics; 13 | 14 | export function getCldOgImageUrl(options: GetCldOgImageUrlOptions, config?: GetCldOgImageUrlConfig, analytics?: GetCldOgImageUrlAnalytics) { 15 | return getCldImageUrl({ 16 | ...options, 17 | format: options.format || 'jpg', 18 | width: options.width || OG_IMAGE_WIDTH, 19 | height: options.height || OG_IMAGE_HEIGHT, 20 | crop: options.crop || { 21 | width: options.width || OG_IMAGE_WIDTH, 22 | height: options.height || OG_IMAGE_HEIGHT, 23 | type: 'fill', 24 | gravity: 'center', 25 | source: true 26 | } 27 | }, config, analytics); 28 | } -------------------------------------------------------------------------------- /astro-cloudinary/src/helpers/getCldVideoUrl.ts: -------------------------------------------------------------------------------- 1 | import { constructCloudinaryUrl } from '@cloudinary-util/url-loader'; 2 | import type { VideoOptions, ConfigOptions, AnalyticsOptions } from '@cloudinary-util/url-loader'; 3 | 4 | import { getCloudinaryConfig, getCloudinaryAnalytics } from "../lib/cloudinary"; 5 | 6 | /** 7 | * getCldVideoUrl 8 | */ 9 | 10 | export type GetCldVideoUrlOptions = VideoOptions; 11 | export type GetCldVideoUrlConfig = ConfigOptions; 12 | export type GetCldVideoUrlAnalytics = AnalyticsOptions; 13 | 14 | export function getCldVideoUrl(options: GetCldVideoUrlOptions, config?: GetCldVideoUrlConfig, analytics?: GetCldVideoUrlAnalytics) { 15 | return constructCloudinaryUrl({ 16 | options: { 17 | assetType: 'video', 18 | format: 'auto:video', 19 | ...options 20 | }, 21 | config: getCloudinaryConfig(config), 22 | analytics: getCloudinaryAnalytics(analytics) 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /astro-cloudinary/src/helpers/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | getCldImageUrl, 3 | type GetCldImageUrlOptions, 4 | type GetCldImageUrlConfig, 5 | type GetCldImageUrlAnalytics 6 | } from './getCldImageUrl.ts'; 7 | 8 | export { 9 | getCldOgImageUrl, 10 | type GetCldOgImageUrlOptions, 11 | type GetCldOgImageUrlConfig, 12 | type GetCldOgImageUrlAnalytics 13 | } from './getCldOgImageUrl.ts'; 14 | 15 | export { 16 | getCldVideoUrl, 17 | type GetCldVideoUrlOptions, 18 | type GetCldVideoUrlConfig, 19 | type GetCldVideoUrlAnalytics 20 | } from './getCldVideoUrl.ts'; -------------------------------------------------------------------------------- /astro-cloudinary/src/lib/cloudinary.ts: -------------------------------------------------------------------------------- 1 | import type { AnalyticsOptions, ConfigOptions } from "@cloudinary-util/url-loader"; 2 | 3 | import { ASTRO_CLOUDINARY_ANALYTICS_PRODUCT_ID, ASTRO_CLOUDINARY_ANALYTICS_ID, ASTRO_CLOUDINARY_VERSION, ASTRO_VERSION } from '../constants/analytics.js'; 4 | 5 | /** 6 | * getCloudinaryConfig 7 | */ 8 | 9 | export function getCloudinaryConfig(config?: ConfigOptions): ConfigOptions { 10 | const cloudName = config?.cloud?.cloudName ?? import.meta.env.PUBLIC_CLOUDINARY_CLOUD_NAME; 11 | 12 | if (!cloudName) { 13 | throw new Error('A Cloudinary Cloud name is required, please make sure PUBLIC_CLOUDINARY_CLOUD_NAME is set and configured in your environment.'); 14 | } 15 | 16 | const apiKey = config?.cloud?.apiKey ?? import.meta.env.PUBLIC_CLOUDINARY_API_KEY; 17 | const secureDistribution = config?.url?.secureDistribution ?? import.meta.env.PUBLIC_CLOUDINARY_SECURE_DISTRIBUTION; 18 | const privateCdn = config?.url?.privateCdn ?? import.meta.env.PUBLIC_CLOUDINARY_PRIVATE_CDN; 19 | 20 | return Object.assign({ 21 | cloud: { 22 | ...config?.cloud, 23 | apiKey, 24 | cloudName 25 | }, 26 | url: { 27 | ...config?.url, 28 | secureDistribution, 29 | privateCdn 30 | } 31 | }, config); 32 | } 33 | 34 | /** 35 | * getCloudinaryAnalytics 36 | */ 37 | 38 | export function getCloudinaryAnalytics(analytics?: AnalyticsOptions) { 39 | return Object.assign({ 40 | product: ASTRO_CLOUDINARY_ANALYTICS_PRODUCT_ID, 41 | sdkCode: ASTRO_CLOUDINARY_ANALYTICS_ID, 42 | sdkSemver: ASTRO_CLOUDINARY_VERSION, 43 | techVersion: ASTRO_VERSION, 44 | feature: '' 45 | }, analytics) 46 | } 47 | -------------------------------------------------------------------------------- /astro-cloudinary/src/lib/loader.ts: -------------------------------------------------------------------------------- 1 | // How can i get this directly from unpic? UrlTransformerOptions 2 | 3 | import { type ImageOptions, type ConfigOptions } from "@cloudinary-util/url-loader"; 4 | import { getCldImageUrl } from "../helpers/getCldImageUrl"; 5 | 6 | interface TransformUrlOptions { 7 | url: string | URL; 8 | width?: number; 9 | height?: number; 10 | } 11 | 12 | export function generateImageLoader(props: ImageOptions, config?: ConfigOptions) { 13 | const imageProps = { ...props }; 14 | 15 | // Normalize width and height to number to allow flexibility in how the values 16 | // are passed through as props 17 | 18 | imageProps.width = typeof imageProps.width === 'string' ? Number.parseInt(imageProps.width) : imageProps.width; 19 | imageProps.height = typeof imageProps.height === 'string' ? Number.parseInt(imageProps.height) : imageProps.height; 20 | 21 | return function loader(loaderOptions: TransformUrlOptions) { 22 | const options = { 23 | ...imageProps, 24 | src: loaderOptions.url, 25 | width: loaderOptions.width, 26 | height: loaderOptions.height, 27 | } as ImageOptions; 28 | 29 | // The loader options are used to create dynamic sizing when working with responsive images 30 | // so these should override the default options collected from the props alone if 31 | // the results are different. While we don't always use the height in the loader logic, 32 | // we always pass it here, as the usage is determined later based on cropping.js 33 | 34 | if ( typeof loaderOptions?.width === 'number' && typeof options.width === 'number' && loaderOptions.width !== options.width ) { 35 | const multiplier = loaderOptions.width / options.width; 36 | 37 | options.width = loaderOptions.width; 38 | 39 | // The height may change based off of the value passed through via the loader options 40 | // In an example where the user sizes is 800x600, but the loader is passing in 400 41 | // due to responsive sizing, we want to ensure we're using a height that will 42 | // resolve to the same aspect ratio 43 | 44 | if ( typeof options.height === 'number' ) { 45 | options.height = Math.floor(options.height * multiplier); 46 | } 47 | } else if ( typeof loaderOptions?.width === 'number' && typeof options?.width !== 'number' ) { 48 | // If we don't have an explicitly defined width, we still need to define a width for sizing optimization but also 49 | // for responsive sizing to take effect, so we can utilize the loader width for the base width 50 | // Note: This is primarily an artifact from Next.js, is this applicable here? 51 | options.width = loaderOptions?.width; 52 | } 53 | 54 | return getCldImageUrl(options, config) 55 | } 56 | } -------------------------------------------------------------------------------- /astro-cloudinary/src/lib/resources.ts: -------------------------------------------------------------------------------- 1 | import type { CloudinaryResource, CloudinaryResourceResourceType } from '@cloudinary-util/types'; 2 | import { ASTRO_CLOUDINARY_VERSION } from '../constants/analytics'; 3 | 4 | /** 5 | * cldRequest 6 | * @description Wrapper for fetch requests with Cloudinary API and credentials 7 | */ 8 | 9 | export async function cldRequest(path: string) { 10 | return fetch(`https://api.cloudinary.com/v1_1/${import.meta.env.PUBLIC_CLOUDINARY_CLOUD_NAME}${path}`, { 11 | headers: { 12 | 'Authorization': 'Basic ' + btoa(`${import.meta.env.PUBLIC_CLOUDINARY_API_KEY}:${import.meta.env.CLOUDINARY_API_SECRET}`), 13 | 'User-Agent' : `CloudinaryAstro/${ASTRO_CLOUDINARY_VERSION}` 14 | } 15 | }); 16 | } 17 | 18 | /** 19 | * getEnvironmentConfig 20 | * @description Get's a Cloudinary product environment's configuration 21 | */ 22 | 23 | export async function getEnvironmentConfig() { 24 | const params = new URLSearchParams(); 25 | 26 | params.append('settings', 'true'); 27 | 28 | const response = await cldRequest(`/config?${params}`); 29 | 30 | if ( !response.ok ) { 31 | throw new Error('Failed to get product environment.'); 32 | } 33 | 34 | const data = await response.json(); 35 | 36 | return data; 37 | } 38 | 39 | /** 40 | * listResources 41 | * @description 42 | */ 43 | 44 | export interface ListResourcesOptions { 45 | deliveryType: 'upload' | 'fetch' | 'private' | 'authenticated' | 'sprite' | 'facebook' | 'twitter' | 'youtube' | 'vimeo'; 46 | fields?: Array; 47 | folder?: CloudinaryResource["folder"]; 48 | folderMode?: string; 49 | limit?: number; 50 | nextCursor?: string; 51 | resourceType: Omit; 52 | 53 | // Additional data 54 | context?: boolean; 55 | metadata?: boolean; 56 | moderation?: boolean; 57 | tags?: boolean; 58 | } 59 | 60 | export interface ListResourcesResponse { 61 | resources: Array; 62 | next_cursor: string; 63 | } 64 | 65 | export async function listResources(options: ListResourcesOptions): Promise { 66 | const params = new URLSearchParams(); 67 | 68 | if ( options.nextCursor ) { 69 | params.append('next_cursor', options.nextCursor); 70 | } 71 | 72 | if ( options.limit ) { 73 | params.append('max_results', `${options.limit}`); 74 | } 75 | 76 | params.append('type', options.deliveryType); 77 | 78 | // There are a few options that are simple booleans that help 79 | // include specific datapoints that aren't included by default 80 | 81 | const contentOptions = ['context', 'metadata', 'moderation', 'tags']; 82 | 83 | for ( let contentOption of contentOptions ) { 84 | const value = options[contentOption as keyof ListResourcesOptions]; 85 | if ( typeof value === 'boolean' ) { 86 | params.append(contentOption, `${value}`); 87 | } 88 | } 89 | 90 | if ( Array.isArray(options.fields) ) { 91 | const fields = options.fields.join(','); 92 | params.append('fields', fields); 93 | } 94 | 95 | let response; 96 | 97 | if ( options.folder ) { 98 | if ( options.folderMode === 'dynamic' ) { 99 | 100 | params.append('asset_folder', options.folder); 101 | 102 | response = await cldRequest(`/resources/by_asset_folder?${params}`); 103 | 104 | } else if ( options.folderMode === 'fixed' ) { 105 | 106 | params.append('prefix', options.folder); 107 | 108 | response = await cldRequest(`/resources/${options.resourceType}?${params}`); 109 | 110 | } else { 111 | throw new Error(`Unhandled folder mode: ${options.folderMode}`); 112 | } 113 | } else { 114 | response = await cldRequest(`/resources/${options.resourceType}?${params}`); 115 | } 116 | 117 | if ( !response.ok ) { 118 | throw new Error(`Failed to list resources - ${response.statusText}`); 119 | } 120 | 121 | const data = await response.json(); 122 | 123 | return data; 124 | } -------------------------------------------------------------------------------- /astro-cloudinary/src/lib/util.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * triggerOnIdle 3 | * @see MDN Polyfill https://github.com/behnammodi/polyfill/blob/master/window.polyfill.js#L7-L24 4 | */ 5 | 6 | export function triggerOnIdle(callback: any) { 7 | if ( window && 'requestIdleCallback' in window ) { 8 | return requestIdleCallback(callback); 9 | } 10 | return setTimeout(() => callback(), 1); 11 | } 12 | 13 | /** 14 | * loadScript 15 | */ 16 | 17 | export function loadScript(src: string) { 18 | return new Promise((resolve, reject) => { 19 | if ( typeof document === 'undefined' ) { 20 | reject('Can not be loaded on') 21 | } 22 | 23 | const script = document.createElement('script'); 24 | 25 | script.src = src; 26 | script.async = true; 27 | 28 | document.body.appendChild(script); 29 | 30 | function handleOnLoad() { 31 | cleanup(); 32 | resolve({ 33 | script 34 | }); 35 | } 36 | 37 | function handleOnError() { 38 | cleanup(); 39 | reject({ 40 | script 41 | }) 42 | } 43 | 44 | script.addEventListener('load', handleOnLoad); 45 | script.addEventListener('error', handleOnError); 46 | 47 | function cleanup() { 48 | script.removeEventListener('load', handleOnLoad); 49 | script.removeEventListener('error', handleOnError); 50 | } 51 | }) 52 | } -------------------------------------------------------------------------------- /astro-cloudinary/src/loaders/assets-loader.ts: -------------------------------------------------------------------------------- 1 | import { AstroError } from "astro/errors"; 2 | import type { Loader } from "astro/loaders"; 3 | import type { CloudinaryResource } from '@cloudinary-util/types' 4 | 5 | import { cloudinaryResourceSchema } from '../types/resources'; 6 | import { getEnvironmentConfig, listResources, type ListResourcesOptions, type ListResourcesResponse } from '../lib/resources'; 7 | 8 | const CLOUDINARY_DEFAULT_LIMIT = 10; 9 | 10 | const REQUIRED_CREDENTIALS = [ 11 | 'PUBLIC_CLOUDINARY_CLOUD_NAME', 12 | 'PUBLIC_CLOUDINARY_API_KEY', 13 | 'CLOUDINARY_API_SECRET', 14 | ]; 15 | 16 | export interface CloudinaryAssetsLoaderOptions { 17 | deliveryType?: ListResourcesOptions["deliveryType"]; 18 | fields?: ListResourcesOptions["fields"]; 19 | folder?: ListResourcesOptions["folder"]; 20 | limit?: ListResourcesOptions["limit"]; 21 | resourceType?: ListResourcesOptions["resourceType"]; 22 | 23 | // Resource data options: These are used to include 24 | // additional data that isn't added by default 25 | context?: ListResourcesOptions["context"]; 26 | metadata?: ListResourcesOptions["metadata"]; 27 | moderation?: ListResourcesOptions["moderation"]; 28 | tags?: ListResourcesOptions["tags"]; 29 | } 30 | 31 | export function cldAssetsLoader(options?: CloudinaryAssetsLoaderOptions): Loader { 32 | return { 33 | name: "cloudinary-assets-loader", 34 | load: async ({ store, logger, generateDigest }) => { 35 | REQUIRED_CREDENTIALS.forEach(CREDENTIAL => { 36 | if ( typeof import.meta.env[CREDENTIAL] === 'undefined' ) { 37 | throw new AstroError(`Missing ${CREDENTIAL}. Please set it as an environment variable inside of your .env.`); 38 | } 39 | }); 40 | 41 | const { settings: { folder_mode: folderMode } } = await getEnvironmentConfig(); 42 | 43 | logger.info(`Loading Cloudinary Assets`); 44 | 45 | // 10 is the Cloudinary default max_results 46 | 47 | const { 48 | context, 49 | deliveryType = 'upload', 50 | fields, 51 | folder, 52 | limit = CLOUDINARY_DEFAULT_LIMIT, 53 | metadata, 54 | moderation, 55 | resourceType = 'image', 56 | tags, 57 | } = options || {}; 58 | let resources: Array = []; 59 | let totalAssetsLoaded = 0; 60 | let nextCursor: string | undefined = undefined; 61 | 62 | const requestOptions = { 63 | context, 64 | deliveryType, 65 | fields, 66 | folder, 67 | folderMode, 68 | limit, 69 | metadata, 70 | moderation, 71 | resourceType, 72 | tags, 73 | } 74 | 75 | // @TODO add `query` option and just check for its existance instead of type 76 | // if ( type === 'list' ) { 77 | while (totalAssetsLoaded < limit) { 78 | let data: ListResourcesResponse | undefined = undefined; 79 | 80 | try { 81 | data = await listResources({ 82 | ...requestOptions, 83 | nextCursor 84 | }); 85 | if ( !data ) { 86 | throw new Error('Unkown error.'); 87 | } 88 | } catch(error) { 89 | logger.error(`${error}`); 90 | // @TODO Should we throw error for generic issues or bail and let the app continue? 91 | return; 92 | } 93 | 94 | // We don't want to exceed the the number of resources specific in the limit 95 | // so make sure that we're only grabbing enough to fill the limit if we're 96 | // to the point where it would be less than the response 97 | 98 | const newResources = data.resources.slice(0, limit - totalAssetsLoaded); 99 | 100 | resources = [...resources, ...newResources]; 101 | totalAssetsLoaded += newResources.length; 102 | nextCursor = data.next_cursor 103 | 104 | if (!nextCursor) { 105 | break; 106 | } 107 | } 108 | 109 | for ( const resource of resources ) { 110 | store.set({ 111 | id: resource.public_id, 112 | data: resource, 113 | digest: generateDigest(resource), 114 | // @TODO could this be used to render an = z.union([ 8 | z.enum(["public", "authenticated"]), 9 | z.intersection(z.string(), z.object({})) 10 | ]); 11 | 12 | export const cloudinaryResourceContextSchema: z.ZodType = z.object({ 13 | custom: z.object({ 14 | alt: z.string().optional(), 15 | caption: z.string().optional(), 16 | }) 17 | .and( 18 | z.record(z.string().optional()) 19 | ), 20 | }); 21 | 22 | export const cloudinaryResourceDeliveryTypeSchema: z.ZodType = z.union([ 23 | z.enum(["animoto", "asset", "authenticated", "dailymotion", "facebook", "fetch", "gravatar", "hulu", "instagram", "list", "multi", "private", "text", "twitter", "twitter_name", "upload", "vimeo", "worldstarhiphop", "youtube"]), 24 | z.intersection(z.string(), z.object({})) 25 | ]); 26 | 27 | export const cloudinaryResourceResourceTypeSchema: z.ZodType = z.union([ 28 | z.enum(["image", "video", "raw", "auto"]), 29 | z.intersection(z.string(), z.object({})) 30 | ]); 31 | 32 | export const cloudinaryResourceSchema: z.ZodType = z.object({ 33 | access_mode: cloudinaryResourceAccessModeSchema.optional(), 34 | access_control: z.array(z.string()).optional(), 35 | asset_id: z.string(), 36 | backup: z.boolean().optional(), 37 | bytes: z.number(), 38 | context: cloudinaryResourceContextSchema.optional(), 39 | colors: z.array(z.tuple([z.string(), z.number()])).optional(), 40 | coordinates: z.object({}).passthrough().optional(), 41 | created_at: z.string(), 42 | derived: z.array(z.string()).optional(), 43 | display_name: z.string().optional(), 44 | exif: z.object({}).passthrough().optional(), 45 | faces: z.array(z.array(z.number())).optional(), 46 | folder: z.string(), 47 | format: z.string(), 48 | height: z.number(), 49 | image_metadata: z.object({}).passthrough().optional(), 50 | info: z.object({}).passthrough().optional(), 51 | media_metadata: z.object({}).passthrough().optional(), 52 | metadata: z.object({}).passthrough().optional(), 53 | moderation: z.union([ z.object({}).passthrough(), z.array(z.string()) ]).optional(), 54 | pages: z.number().optional(), 55 | phash: z.string().optional(), 56 | placeholder: z.boolean().optional(), 57 | predominant: z.object({}).passthrough().optional(), 58 | public_id: z.string(), 59 | quality_analysis: z.number().optional(), 60 | resource_type: cloudinaryResourceResourceTypeSchema, 61 | secure_url: z.string(), 62 | signature: z.string().optional(), 63 | tags: z.array(z.string()).optional(), 64 | type: cloudinaryResourceDeliveryTypeSchema, 65 | url: z.string(), 66 | version: z.number(), 67 | width: z.number() 68 | }).passthrough(); -------------------------------------------------------------------------------- /astro-cloudinary/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/strict", 3 | "compilerOptions": { 4 | "noEmit": false, 5 | "declaration": true, 6 | "emitDeclarationOnly": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /astro-cloudinary/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup' 2 | 3 | export default defineConfig(() => { 4 | return { 5 | clean: true, 6 | entry: [ 7 | './src/helpers/index.ts', 8 | './src/loaders/index.ts', 9 | ], 10 | dts: true, 11 | minify: false, 12 | bundle: true, 13 | sourcemap: true, 14 | format: ['esm'], 15 | external: ['astro', 'node:async_hooks', '#async-local-storage'], 16 | }; 17 | }); -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | # generated types 4 | .astro/ 5 | 6 | # dependencies 7 | node_modules/ 8 | 9 | # logs 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | pnpm-debug.log* 14 | 15 | 16 | # environment variables 17 | .env 18 | .env.production 19 | 20 | # macOS-specific files 21 | .DS_Store 22 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Starlight Starter Kit: Basics 2 | 3 | [![Built with Starlight](https://astro.badg.es/v2/built-with-starlight/tiny.svg)](https://starlight.astro.build) 4 | 5 | ``` 6 | npm create astro@latest -- --template starlight 7 | ``` 8 | 9 | [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/starlight/tree/main/examples/basics) 10 | [![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/starlight/tree/main/examples/basics) 11 | [![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/withastro/starlight&create_from_path=examples/basics) 12 | [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fwithastro%2Fstarlight%2Ftree%2Fmain%2Fexamples%2Fbasics&project-name=my-starlight-docs&repository-name=my-starlight-docs) 13 | 14 | > 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun! 15 | 16 | ## 🚀 Project Structure 17 | 18 | Inside of your Astro + Starlight project, you'll see the following folders and files: 19 | 20 | ``` 21 | . 22 | ├── public/ 23 | ├── src/ 24 | │ ├── assets/ 25 | │ ├── content/ 26 | │ │ ├── docs/ 27 | │ │ └── config.ts 28 | │ └── env.d.ts 29 | ├── astro.config.mjs 30 | ├── package.json 31 | └── tsconfig.json 32 | ``` 33 | 34 | Starlight looks for `.md` or `.mdx` files in the `src/content/docs/` directory. Each file is exposed as a route based on its file name. 35 | 36 | Images can be added to `src/assets/` and embedded in Markdown with a relative link. 37 | 38 | Static assets, like favicons, can be placed in the `public/` directory. 39 | 40 | ## 🧞 Commands 41 | 42 | All commands are run from the root of the project, from a terminal: 43 | 44 | | Command | Action | 45 | | :------------------------ | :----------------------------------------------- | 46 | | `npm install` | Installs dependencies | 47 | | `npm run dev` | Starts local dev server at `localhost:4321` | 48 | | `npm run build` | Build your production site to `./dist/` | 49 | | `npm run preview` | Preview your build locally, before deploying | 50 | | `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | 51 | | `npm run astro -- --help` | Get help using the Astro CLI | 52 | 53 | ## 👀 Want to learn more? 54 | 55 | Check out [Starlight’s docs](https://starlight.astro.build/), read [the Astro documentation](https://docs.astro.build), or jump into the [Astro Discord server](https://astro.build/chat). 56 | -------------------------------------------------------------------------------- /docs/astro.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'astro/config'; 2 | import starlight from '@astrojs/starlight'; 3 | import tailwind from "@astrojs/tailwind"; 4 | 5 | import icon from "astro-icon"; 6 | 7 | import vercel from '@astrojs/vercel/serverless'; 8 | 9 | export default defineConfig({ 10 | 11 | integrations: [ 12 | starlight({ 13 | title: 'Astro Cloudinary', 14 | social: { 15 | github: 'https://github.com/cloudinary-community/astro-cloudinary' 16 | }, 17 | sidebar: [ 18 | { 19 | label: 'Installation', 20 | link: 'installation' 21 | }, 22 | { 23 | label: 'Components', 24 | items: [ 25 | { 26 | label: 'CldImage', 27 | items: [ 28 | { 29 | label: 'Basic Usage', 30 | slug: 'cldimage/basic-usage' 31 | }, 32 | { 33 | label: 'Configuration', 34 | slug: 'cldimage/configuration' 35 | }, 36 | { 37 | label: 'Examples', 38 | slug: 'cldimage/examples' 39 | } 40 | ] 41 | }, 42 | { 43 | label: 'CldOgImage', 44 | items: [ 45 | { 46 | label: 'Basic Usage', 47 | slug: 'cldogimage/basic-usage' 48 | }, 49 | { 50 | label: 'Configuration', 51 | slug: 'cldogimage/configuration' 52 | } 53 | ] 54 | }, 55 | { 56 | label: 'CldUploadWidget', 57 | items: [ 58 | { 59 | label: 'Basic Usage', 60 | slug: 'clduploadwidget/basic-usage' 61 | }, 62 | { 63 | label: 'Configuration', 64 | slug: 'clduploadwidget/configuration' 65 | }, 66 | { 67 | label: 'Signed Uploads', 68 | slug: 'clduploadwidget/signed-uploads' 69 | }, 70 | { 71 | label: 'Examples', 72 | slug: 'clduploadwidget/examples' 73 | } 74 | ] 75 | }, 76 | { 77 | label: 'CldVideoPlayer', 78 | items: [ 79 | { 80 | label: 'Basic Usage', 81 | slug: 'cldvideoplayer/basic-usage' 82 | }, 83 | { 84 | label: 'Configuration', 85 | slug: 'cldvideoplayer/configuration' 86 | }, 87 | { 88 | label: 'Examples', 89 | slug: 'cldvideoplayer/examples' 90 | } 91 | ] 92 | }, 93 | ] 94 | }, 95 | { 96 | label: 'Helpers', 97 | items: [ 98 | { 99 | label: 'getCldImageUrl', 100 | items: [ 101 | { 102 | label: 'Basic Usage', 103 | slug: 'getcldimageurl/basic-usage' 104 | }, 105 | { 106 | label: 'Configuration', 107 | slug: 'getcldimageurl/configuration' 108 | }, 109 | { 110 | label: 'Examples', 111 | slug: 'getcldimageurl/examples' 112 | } 113 | ] 114 | }, 115 | { 116 | label: 'getCldOgImageUrl', 117 | items: [ 118 | { 119 | label: 'Basic Usage', 120 | slug: 'getcldogimageurl/basic-usage' 121 | }, 122 | { 123 | label: 'Configuration', 124 | slug: 'getcldogimageurl/configuration' 125 | }, 126 | { 127 | label: 'Examples', 128 | slug: 'getcldogimageurl/examples' 129 | } 130 | ] 131 | }, 132 | { 133 | label: 'getCldVideoUrl', 134 | items: [ 135 | { 136 | label: 'Basic Usage', 137 | slug: 'getcldvideourl/basic-usage' 138 | }, 139 | { 140 | label: 'Configuration', 141 | slug: 'getcldvideourl/configuration' 142 | }, 143 | { 144 | label: 'Examples', 145 | slug: 'getcldvideourl/examples' 146 | } 147 | ] 148 | }, 149 | ] 150 | }, 151 | { 152 | label: 'Loaders', 153 | items: [ 154 | { 155 | label: 'cldAssetsLoader', 156 | items: [ 157 | { 158 | label: 'Basic Usage', 159 | slug: 'cldassetsloader/basic-usage' 160 | }, 161 | { 162 | label: 'Configuration', 163 | slug: 'cldassetsloader/configuration' 164 | }, 165 | { 166 | label: 'Examples', 167 | slug: 'cldassetsloader/examples' 168 | }, 169 | ] 170 | }, 171 | ] 172 | }, 173 | { 174 | label: 'Guides', 175 | items: [ 176 | { 177 | label: 'Background Removal', 178 | slug: 'guides/background-removal' 179 | }, 180 | { 181 | label: 'Delivering Remote Images', 182 | slug: 'guides/delivering-remote-images' 183 | }, 184 | { 185 | label: 'Image Optimization', 186 | slug: 'guides/image-optimization' 187 | }, 188 | { 189 | label: 'Image Overlays', 190 | slug: 'guides/image-overlays' 191 | }, 192 | { 193 | label: 'Responsive Images', 194 | slug: 'guides/responsive-images' 195 | }, 196 | { 197 | label: 'Social Media Card', 198 | slug: 'guides/social-media-card' 199 | }, 200 | { 201 | label: 'Text Overlays', 202 | slug: 'guides/text-overlays' 203 | }, 204 | { 205 | label: 'Uploading Images & Videos', 206 | slug: 'guides/uploading-images-and-videos' 207 | }, 208 | ] 209 | }, 210 | { 211 | label: 'Templates', 212 | items: [ 213 | { 214 | label: 'Social Media Cards', 215 | slug: 'templates/social-media-cards' 216 | } 217 | ] 218 | } 219 | ], 220 | customCss: ['./src/styles/tailwind.css'], 221 | components: { 222 | Hero: './src/components/Null.astro', 223 | Head: './src/components/Head.astro', 224 | }, 225 | }), 226 | tailwind({ 227 | applyBaseStyles: false 228 | }), 229 | icon() 230 | ], 231 | 232 | redirects: { 233 | '/guides/responsive-sizing': '/guides/responsive-images' 234 | }, 235 | 236 | adapter: vercel({ 237 | webAnalytics: { 238 | enabled: true, 239 | } 240 | }), 241 | }); 242 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "type": "module", 4 | "version": "0.0.1", 5 | "scripts": { 6 | "dev": "astro dev", 7 | "start": "astro dev", 8 | "build": "astro check && astro build", 9 | "preview": "astro preview", 10 | "astro": "astro" 11 | }, 12 | "dependencies": { 13 | "@astrojs/check": "^0.9.4", 14 | "@astrojs/starlight": "^0.32.3", 15 | "@astrojs/starlight-tailwind": "^3.0.0", 16 | "@astrojs/tailwind": "^6.0.0", 17 | "@astrojs/vercel": "^8.1.2", 18 | "@vercel/analytics": "^1.3.1", 19 | "astro": "^5.5.3", 20 | "astro-icon": "^1.1.1", 21 | "cloudinary": "^2.5.0", 22 | "clsx": "^2.1.1", 23 | "sharp": "^0.33.5", 24 | "tailwind-merge": "^2.5.2", 25 | "tailwindcss": "^3.4.13", 26 | "typescript": "^5.6.2" 27 | }, 28 | "devDependencies": { 29 | "@iconify-json/mdi": "^1.2.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /docs/public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudinary-community/astro-cloudinary/7680d1fce1f43d5159a4181e672013280f18d258/docs/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /docs/public/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudinary-community/astro-cloudinary/7680d1fce1f43d5159a4181e672013280f18d258/docs/public/android-chrome-512x512.png -------------------------------------------------------------------------------- /docs/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudinary-community/astro-cloudinary/7680d1fce1f43d5159a4181e672013280f18d258/docs/public/apple-touch-icon.png -------------------------------------------------------------------------------- /docs/public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #da532c 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/public/favicon-1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudinary-community/astro-cloudinary/7680d1fce1f43d5159a4181e672013280f18d258/docs/public/favicon-1024x1024.png -------------------------------------------------------------------------------- /docs/public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudinary-community/astro-cloudinary/7680d1fce1f43d5159a4181e672013280f18d258/docs/public/favicon-16x16.png -------------------------------------------------------------------------------- /docs/public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudinary-community/astro-cloudinary/7680d1fce1f43d5159a4181e672013280f18d258/docs/public/favicon-32x32.png -------------------------------------------------------------------------------- /docs/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudinary-community/astro-cloudinary/7680d1fce1f43d5159a4181e672013280f18d258/docs/public/favicon.ico -------------------------------------------------------------------------------- /docs/public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Astro Cloudinary", 3 | "short_name": "Astro Cloudinary", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /docs/src/assets/houston.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudinary-community/astro-cloudinary/7680d1fce1f43d5159a4181e672013280f18d258/docs/src/assets/houston.webp -------------------------------------------------------------------------------- /docs/src/components/Button.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { cn } from '../lib/util.js'; 3 | 4 | interface Props { 5 | class?: string; 6 | href: string; 7 | } 8 | 9 | const { class: className = '', href }: Props = Astro.props; 10 | const buttonColor = '!text-white !bg-blue-500 hover:!bg-blue-600'; 11 | 12 | const buttonProps = { 13 | class: cn( 14 | 'inline-block rounded py-2.5 px-6 text-lg font-bold uppercase no-underline', 15 | buttonColor, 16 | className 17 | ) 18 | } 19 | --- 20 | {href && ( 21 | 22 | 23 | 24 | )} 25 | 26 | {!href && ( 27 | 30 | )} 31 | -------------------------------------------------------------------------------- /docs/src/components/CodeBlock.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { Icon } from 'astro-icon/components' 3 | 4 | import { cn } from '../lib/util'; 5 | 6 | interface Props { 7 | class?: string; 8 | alwaysExpanded?: boolean; 9 | }; 10 | 11 | const { class: className, alwaysExpanded = false }: Props = Astro.props; 12 | 13 | const codeBlockClassName = cn('code-block group relative [&_.code-block-code_pre]:rounded [&_.code-block-code_pre]:border-slate-200 dark:[&_.code-block-code_pre]:border-slate-600', className); 14 | --- 15 | 16 |
17 |
18 | 19 |
20 | {!alwaysExpanded && ( 21 |
    22 |
  • 23 | 27 |
  • 28 |
  • 29 | 33 |
  • 34 |
35 | )} 36 |
37 | 38 | 53 | 54 | -------------------------------------------------------------------------------- /docs/src/components/DemoAssetsLoaderEcommerce.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { getCollection } from 'astro:content'; 3 | 4 | import { CldImage } from '../../../astro-cloudinary'; 5 | 6 | const assets = await getCollection('assetsSamplesEcommerce'); 7 | --- 8 |
    9 | {assets.map(asset => { 10 | return ( 11 |
  • 12 | 43 |
  • 44 | ) 45 | })} 46 |
-------------------------------------------------------------------------------- /docs/src/components/DemoAssetsLoaderSamplesBasic.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { getCollection } from 'astro:content'; 3 | 4 | import { CldImage } from '../../../astro-cloudinary'; 5 | 6 | const assets = await getCollection('assetsSamplesBasic'); 7 | --- 8 | 9 |
    10 | {assets.map(asset => { 11 | return ( 12 |
  • 13 | 22 |
  • 23 | ) 24 | })} 25 |
-------------------------------------------------------------------------------- /docs/src/components/DemoAssetsLoaderSamplesDefault.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { getCollection } from 'astro:content'; 3 | 4 | import { CldImage } from '../../../astro-cloudinary'; 5 | 6 | const allAssets = await getCollection('assetsSamplesDefault'); 7 | const assets = allAssets.slice(0, 8); 8 | --- 9 |
10 |
    11 | {assets.map(asset => { 12 | return ( 13 |
  • 14 | 26 |
  • 27 | ) 28 | })} 29 |
30 | 31 |

32 | Not displaying all assets for performance. 33 |

34 | 35 |

36 | Total Assets in Collection: { allAssets.length } 37 |

38 |
-------------------------------------------------------------------------------- /docs/src/components/DemoAssetsLoaderSamplesLimit.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { getCollection } from 'astro:content'; 3 | 4 | import { CldImage } from '../../../astro-cloudinary'; 5 | 6 | const allAssets = await getCollection('assetsSamplesLimit'); 7 | const assets = allAssets.slice(0, 20); 8 | --- 9 |
10 |
11 |
    12 | {assets.map(asset => { 13 | return ( 14 |
  • 15 | 24 |
  • 25 | ) 26 | })} 27 |
28 |
29 | 30 |

31 | Not displaying all assets for performance. 32 |

33 | 34 |

35 | Total Assets in Collection: { allAssets.length } 36 |

37 |
-------------------------------------------------------------------------------- /docs/src/components/DemoAssetsLoaderSamplesSingle.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { getEntry } from 'astro:content'; 3 | 4 | import { CldImage } from '../../../astro-cloudinary'; 5 | 6 | const asset = await getEntry('assetsSamplesLimit', 'collection/a7ruc9xysr0lkxip4cgg'); 7 | --- 8 | {asset && ( 9 | 17 | )} -------------------------------------------------------------------------------- /docs/src/components/DemoImageEvents.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { CldImage, type CldImageProps } from '../../../astro-cloudinary'; 3 | 4 | interface Props extends CldImageProps {}; 5 | 6 | const props: Props = Astro.props; 7 | --- 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/src/components/DemoUploadWidgetSigned.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { type CldUploadWidgetProps } from '../../../astro-cloudinary'; 3 | import DemoUploadWidgetUnsigned from './DemoUploadWidgetUnsigned.astro'; 4 | 5 | interface Props extends CldUploadWidgetProps {}; 6 | 7 | const props: Props = Astro.props; 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /docs/src/components/DemoUploadWidgetUnsigned.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { CldUploadWidget, type CldUploadWidgetProps } from '../../../astro-cloudinary'; 3 | import HeaderImage from './HeaderImage.astro'; 4 | 5 | interface Props extends CldUploadWidgetProps {}; 6 | 7 | const props: Props = Astro.props; 8 | --- 9 | 10 |
11 | 12 |

13 | 14 |

15 |
16 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /docs/src/components/DemoVideoPlayerEvents.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { CldVideoPlayer, type CldVideoPlayerProps } from '../../../astro-cloudinary'; 3 | 4 | interface Props extends CldVideoPlayerProps {}; 5 | 6 | const props: Props = Astro.props; 7 | --- 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/src/components/ExamplesCldOgImage.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { CldImage, type CldImageProps } from '../../../astro-cloudinary'; 3 | import { OG_IMAGE_WIDTH, OG_IMAGE_HEIGHT } from '../../../astro-cloudinary/src/constants/sizes'; 4 | 5 | interface Props extends CldImageProps {} 6 | 7 | const props: Props = Astro.props; 8 | --- 9 | -------------------------------------------------------------------------------- /docs/src/components/Head.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import type { Props } from '@astrojs/starlight/props' 3 | import Default from '@astrojs/starlight/components/Head.astro' 4 | import { CldOgImage } from '../../../astro-cloudinary'; 5 | 6 | const { slug, entry } = Astro.locals.starlightRoute; 7 | const { title, ogImageTitle } = entry.data; 8 | --- 9 | 10 | 11 | {slug === '' && ( 12 | 17 | )} 18 | {slug !== '' && ( 19 | 60 | )} -------------------------------------------------------------------------------- /docs/src/components/HeaderImage.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { cn } from '../lib/util'; 3 | 4 | interface Props { 5 | class?: string; 6 | layout?: string; 7 | }; 8 | 9 | const { layout, class: className }: Props = Astro.props; 10 | --- 11 |
16 |
26 | 27 |
28 |
-------------------------------------------------------------------------------- /docs/src/components/ImageGrid.astro: -------------------------------------------------------------------------------- 1 | --- 2 | interface Props { 3 | columns?: number; 4 | } 5 | 6 | const { columns = 2 }: Props = Astro.props; 7 | --- 8 |
    9 | 10 |
11 | 12 | -------------------------------------------------------------------------------- /docs/src/components/Null.astro: -------------------------------------------------------------------------------- 1 | --- 2 | --- -------------------------------------------------------------------------------- /docs/src/components/Table.astro: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | interface Props { 4 | columns: Array<{ 5 | id: string; 6 | title: string; 7 | }>; 8 | data: {}; 9 | } 10 | 11 | const { columns, data } = Astro.props; 12 | --- 13 | 14 | 15 | {Array.isArray(columns) && ( 16 | 17 | 18 | {columns.map(({ title }) => { 19 | return ( 20 | 23 | ) 24 | })} 25 | 26 | 27 | )} 28 | {Array.isArray(data) && ( 29 | 30 | {data.map((row) => { 31 | return ( 32 | 33 | { columns.map(({ id }, index) => { 34 | let Child = row[id] || ' '; 35 | 36 | if ( typeof Child === 'function' ) { 37 | Child = 38 | } 39 | 40 | if ( index === 0 ) { 41 | return ( 42 | 45 | ) 46 | } 47 | return ( 48 | 51 | ) 52 | })} 53 | 54 | ) 55 | })} 56 | 57 | )} 58 |
21 | { title || ' ' } 22 |
43 | { Child } 44 | 49 | { Child } 50 |
-------------------------------------------------------------------------------- /docs/src/content.config.ts: -------------------------------------------------------------------------------- 1 | import { defineCollection, z } from 'astro:content'; 2 | import { docsLoader } from "@astrojs/starlight/loaders"; 3 | import { docsSchema } from '@astrojs/starlight/schema'; 4 | 5 | import { cldAssetsLoader } from '../../astro-cloudinary/loaders'; 6 | 7 | export const collections = { 8 | assetsSamplesBasic: defineCollection({ 9 | loader: cldAssetsLoader({ 10 | limit: 4, 11 | folder: 'samples/food', 12 | }) 13 | }), 14 | assetsSamplesDefault: defineCollection({ 15 | loader: cldAssetsLoader({ 16 | // Setting an explicit folder to avoid unmaintained root images 17 | folder: 'ecommerce/fashion', 18 | }) 19 | }), 20 | assetsSamplesLimit: defineCollection({ 21 | loader: cldAssetsLoader({ 22 | limit: 1200, 23 | folder: 'collection' 24 | }) 25 | }), 26 | assetsSamplesEcommerce: defineCollection({ 27 | loader: cldAssetsLoader({ 28 | limit: 4, 29 | folder: 'ecommerce/sneakers', 30 | context: true, 31 | tags: true, 32 | moderation: true, 33 | metadata: true, 34 | }) 35 | }), 36 | docs: defineCollection({ 37 | loader: docsLoader(), 38 | schema: docsSchema({ 39 | extend: z.object({ 40 | ogImageTitle: z.string().optional(), 41 | }), 42 | }), 43 | }), 44 | }; 45 | -------------------------------------------------------------------------------- /docs/src/content/docs/cldassetsloader/basic-usage.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started with cldAssetsLoader 3 | description: Query Cloudinary assets in Astro with Collections and cldAssetsLoader. 4 | ogImageTitle: cldAssetsLoader 5 | head: 6 | - tag: title 7 | content: cldAssetsLoader - Astro Cloudinary 8 | --- 9 | import { CldImage } from '../../../../../astro-cloudinary'; 10 | 11 | import CodeBlock from '../../../components/CodeBlock.astro'; 12 | import HeaderImage from '../../../components/HeaderImage.astro'; 13 | import DemoAssetsLoaderSamplesBasic from '../../../components/DemoAssetsLoaderSamplesBasic.astro'; 14 | import DemoAssetsLoaderSamplesSingle from '../../../components/DemoAssetsLoaderSamplesSingle.astro'; 15 | 16 | The Cloudinary Assets Loader allows you to query your Cloudinary images, videos, 17 | and other resources using the [Astro Collections](https://docs.astro.build/en/reference/configuration-reference/#querying-and-rendering-with-the-content-layer-api). 18 | 19 | ## Basic Usage 20 | 21 | `cldAssetsLoader` allows you to specify exactly which assets you'd like to 22 | query and store in an Astro Collection, including a folder and how many 23 | assets in total you would like to request. 24 | 25 | Start by defining a new Collection with `defineCollection`: 26 | 27 | 28 | ```jsx 29 | import { defineCollection } from 'astro:content'; 30 | import { cldAssetsLoader } from 'astro-cloudinary/loaders'; 31 | 32 | export const collections = { 33 | assets: defineCollection({ 34 | loader: cldAssetsLoader({ 35 | limit: 4, 36 | folder: 'samples/food' 37 | }) 38 | }), 39 | } 40 | ``` 41 | 42 | 43 | ## Getting Assets from a Collection 44 | 45 | You can use `getCollection` to retrieve your assets: 46 | 47 | 48 | 49 | 50 | 51 | 52 | ```jsx 53 | --- 54 | import { CldImage } from 'astro-cloudinary'; 55 | import { getCollection } from 'astro:content'; 56 | const assets = await getCollection(''); 57 | --- 58 |
    59 | {assets.map(asset => { 60 | return ( 61 |
  • 62 | 68 |
  • 69 | ) 70 | })} 71 |
72 | ``` 73 |
74 | 75 | By default, using `cldAssetsLoader` will query the assets in the root of your 76 | Cloudinary account, but you can customize the request with options depending 77 | on the assets you want to find. 78 | 79 | ## Getting a Single Asset from a Collection 80 | 81 | You can use `getEntry` to retrieve a single asset: 82 | 83 | 84 | 85 | 86 | 87 | 88 | ```jsx 89 | --- 90 | import { CldImage } from 'astro-cloudinary'; 91 | import { getEntry } from 'astro:content'; 92 | const asset = await getEntry('', ''); 93 | --- 94 | 100 | ``` 101 | 102 | 103 | ## Learn More about CldImage 104 | * [Configuration](/cldassetsloader/configuration) 105 | -------------------------------------------------------------------------------- /docs/src/content/docs/cldassetsloader/configuration.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Configuring cldAssetsLoader 3 | description: Options and configuration for cldAssetsLoader in Astro. 4 | head: 5 | - tag: title 6 | content: cldAssetsLoader Configuration - Astro Cloudinary 7 | --- 8 | import { CldImage } from '../../../../../astro-cloudinary'; 9 | 10 | import CodeBlock from '../../../components/CodeBlock.astro'; 11 | import HeaderImage from '../../../components/HeaderImage.astro'; 12 | import Table from '../../../components/Table.astro'; 13 | 14 | cldAssetLoader provides a wide range of options to customize your Collectoin query. 15 | 16 | ## Basic Options 17 | 18 | (true), 46 | more: () => (More Info) 47 | }, 48 | { 49 | option: 'deliveryType', 50 | type: 'string', 51 | default: 'upload', 52 | example: () => (upload), 53 | more: () => (More Info) 54 | }, 55 | { 56 | option: 'fields', 57 | type: 'Array', 58 | default: '-', 59 | example: () => (['width', 'height', 'context']), 60 | more: () => (More Info) 61 | }, 62 | { 63 | option: 'folder', 64 | type: 'string', 65 | default: '-', 66 | example: () => (samples/food), 67 | more: () => (More Info) 68 | }, 69 | { 70 | option: 'limit', 71 | type: 'number', 72 | default: '10', 73 | example: () => (45), 74 | more: () => (More Info) 75 | }, 76 | { 77 | option: 'metadata', 78 | type: 'boolean', 79 | default: 'false', 80 | example: () => (true), 81 | more: () => (More Info) 82 | }, 83 | { 84 | option: 'moderation', 85 | type: 'boolean', 86 | default: 'false', 87 | example: () => (true), 88 | more: () => (More Info) 89 | }, 90 | { 91 | option: 'resourceType', 92 | type: 'string', 93 | example: () => (45), 94 | default: 'image', 95 | more: () => (More Info) 96 | }, 97 | { 98 | option: 'tags', 99 | type: 'boolean', 100 | default: 'true', 101 | example: () => (true), 102 | more: () => (More Info) 103 | }, 104 | { 105 | option: 'type', 106 | type: 'number', 107 | default: 'list', 108 | example: () => (search), 109 | }, 110 | ]} 111 | /> 112 | 113 | 114 | ### `context` 115 | 116 | Whether to include Contextual Metadata (`context`) in the resource object. 117 | 118 | **Examples** 119 | 120 | 121 | ```jsx 122 | context: true 123 | ``` 124 | 125 | 126 | [Learn more about Contextual Metadata](https://cloudinary.com/documentation/contextual_metadata) in the Cloudinary docs. 127 | 128 | #### `deliveryType` 129 | 130 | The delivery type of the assets you want to query. 131 | 132 | **Examples** 133 | 134 | 135 | ```jsx 136 | deliveryType: 'private' 137 | ``` 138 | 139 | 140 | [Learn more about Delivery Types](https://cloudinary.com/documentation/upload_parameters#delivery_types) on the Cloudinary docs. 141 | 142 | #### `fields` 143 | 144 | The asset fields to include in the resource object response. 145 | 146 | **Examples** 147 | 148 | 149 | ```jsx 150 | fields: ['width', 'height', 'context'] 151 | ``` 152 | 153 | 154 | #### `folder` 155 | 156 | The folder the assets should be queried from. 157 | 158 | **Examples** 159 | 160 | 161 | ```jsx 162 | folder: 'samples/landscapes' 163 | ``` 164 | 165 | 166 | #### `limit` 167 | 168 | The maximum amount of results to query. 169 | 170 | **Examples** 171 | 172 | 173 | ```jsx 174 | limit: 20 175 | ``` 176 | 177 | 178 | ### `metadata` 179 | 180 | Whether to include Structured Metadata (`metadata`) in the resource object. 181 | 182 | **Examples** 183 | 184 | 185 | ```jsx 186 | metadata: true 187 | ``` 188 | 189 | 190 | [Learn more about Structured Metadata](https://cloudinary.com/documentation/structured_metadata) in the Cloudinary docs. 191 | 192 | ### `moderation` 193 | 194 | Whether to include the moderation status (`moderation`) in the resource object. 195 | 196 | **Examples** 197 | 198 | 199 | ```jsx 200 | moderation: true 201 | ``` 202 | 203 | 204 | [Learn more about Moderation on Upload](https://cloudinary.com/documentation/upload_parameters#moderation) in the Cloudinary docs. 205 | 206 | #### `resourceType` 207 | 208 | The type of the assets you want to query. 209 | 210 | **Examples** 211 | 212 | 213 | ```jsx 214 | resourceType: 'video' 215 | ``` 216 | 217 | 218 | [Learn more about Asset Types](https://cloudinary.com/documentation/upload_parameters#asset_types) on the Cloudinary docs. 219 | 220 | ### `tags` 221 | 222 | Whether to include the asset's tags (`tags`) in the resource object. 223 | 224 | **Examples** 225 | 226 | 227 | ```jsx 228 | tags: true 229 | ``` 230 | 231 | 232 | [Learn more about Tags](https://cloudinary.com/documentation/tags) in the Cloudinary docs. 233 | 234 | #### `type` 235 | 236 | The type of query to make for assets, including list or search. 237 | 238 | This primarily differs in how the requests are made, using the Admin API or the Search API. 239 | 240 | **Examples** 241 | 242 | 243 | ```jsx 244 | type: 'search' 245 | ``` 246 | -------------------------------------------------------------------------------- /docs/src/content/docs/cldassetsloader/examples.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: cldAssetsLoader Examples 3 | description: Query for collections of Cloudinary resources with cldAssetsLoader. 4 | head: 5 | - tag: title 6 | content: cldAssetsLoader Examples - Astro Cloudinary 7 | --- 8 | import { cldAssetsLoader } from '../../../../../astro-cloudinary/loaders'; 9 | 10 | import CodeBlock from '../../../components/CodeBlock.astro'; 11 | import HeaderImage from '../../../components/HeaderImage.astro'; 12 | import DemoAssetsLoaderSamplesDefault from '../../../components/DemoAssetsLoaderSamplesDefault.astro'; 13 | import DemoAssetsLoaderSamplesLimit from '../../../components/DemoAssetsLoaderSamplesLimit.astro'; 14 | import DemoAssetsLoaderEcommerce from '../../../components/DemoAssetsLoaderEcommerce.astro'; 15 | 16 | ## Basic Collections 17 | 18 | Cloudinary supports a wide variety of powerful transformations that allow you to 19 | not only deliver, but easily edit and build new images on the fly. 20 | 21 | ### Default 22 | 23 | 24 | 25 | 26 | 27 | Defining the Collection 28 | 29 | 30 | ```jsx 31 | import { cldAssetsLoader } from 'astro-cloudinary/loaders'; 32 | 33 | export const collections = { 34 | assets: defineCollection({ 35 | loader: cldAssetsLoader() 36 | }), 37 | } 38 | ``` 39 | 40 | 41 | 42 | Accessing the Collection 43 | 44 | 45 | ```jsx 46 | --- 47 | import { getCollection } from 'astro:content'; 48 | import { CldImage } from 'astro-cloudinary'; 49 | const assets = await getCollection('assets'); 50 | --- 51 | {assets.map(asset => { 52 | return ( 53 | 59 | ) 60 | })} 61 | ``` 62 | 63 | 64 | ### Large Collections 65 | 66 | 67 | 68 | 69 | 70 | Defining the Collection 71 | 72 | 73 | ```jsx 74 | import { cldAssetsLoader } from 'astro-cloudinary/loaders'; 75 | 76 | export const collections = { 77 | assets: defineCollection({ 78 | loader: cldAssetsLoader({ 79 | limit: 1200, 80 | folder: '' 81 | }) 82 | }), 83 | } 84 | ``` 85 | 86 | 87 | Accessing the Collection 88 | 89 | 90 | ```jsx 91 | --- 92 | import { getCollection } from 'astro:content'; 93 | import { CldImage } from 'astro-cloudinary'; 94 | const assets = await getCollection('assets'); 95 | --- 96 | {assets.map(asset => { 97 | return ( 98 | 104 | ) 105 | })} 106 | ``` 107 | 108 | 109 | ## Advanced 110 | 111 | ### Tags, Context, & Metadata 112 | 113 | 114 | 115 | 116 | 117 | Defining the Collection 118 | 119 | 120 | ```jsx 121 | import { cldAssetsLoader } from 'astro-cloudinary/loaders'; 122 | 123 | export const collections = { 124 | assets: defineCollection({ 125 | loader: cldAssetsLoader({ 126 | context: true, 127 | tags: true, 128 | moderation: true, 129 | metadata: true, 130 | }) 131 | }), 132 | } 133 | ``` 134 | 135 | 136 | Accessing the Collection 137 | 138 | 139 | ```jsx 140 | --- 141 | import { getCollection } from 'astro:content'; 142 | import { CldImage } from 'astro-cloudinary'; 143 | const assets = await getCollection('assets'); 144 | --- 145 | {assets.map(asset => { 146 | return ( 147 | 177 | ) 178 | })} 179 | ``` 180 | -------------------------------------------------------------------------------- /docs/src/content/docs/cldogimage/basic-usage.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started with CldOgImage 3 | description: Auto generate OG images and the relevant tags in Astro with the CldOgImage component. 4 | ogImageTitle: CldOgImage 5 | head: 6 | - tag: title 7 | content: CldOgImage - Astro Cloudinary 8 | --- 9 | import { CldImage } from '../../../../../astro-cloudinary'; 10 | 11 | import CodeBlock from '../../../components/CodeBlock.astro'; 12 | import HeaderImage from '../../../components/HeaderImage.astro'; 13 | import DemoImageEvents from '../../../components/DemoImageEvents.astro'; 14 | 15 | The CldOgImage component provides an easy way to deliver images from Cloudinary in an Astro app. 16 | 17 | With it comes access to more advanced features like dynamic cropping, background removal, overlays, and other Cloudinary transformations. 18 | 19 | As CldImage is a wrapper around the Unpic Image component, you also gain access to built-in Image component features that will work out-of-the-box like [Responsive Sizing](/guides/responsive-images). 20 | 21 | ## Basic Usage 22 | 23 | The basic required props include `twitterTitle`, `src`, and `alt`: 24 | 25 | 26 | 33 | 34 | 35 | > CldOgImage does not render an `` tag, meaning it can't be visually embedded on a page. The following examples make use of the `` tag to showcase what's possible. 36 | 37 | 38 | ```jsx 39 | --- 40 | import { CldOgImage } from 'astro-cloudinary'; 41 | --- 42 | 47 | ``` 48 | 49 | 50 | The resulting HTML will be applied to the Head of the document including all applicable [open graph tags](https://ogp.me/): 51 | 52 | ```html copy 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | ``` 61 | 62 | The `src` property takes in a Cloudinary Public ID which includes the folder path along with the ID of the image itself. 63 | The `twitterTitle` should represent the title of the page and the `alt` value should be a text-based description 64 | of the image. 65 | 66 | ## Additional Features 67 | 68 | You can further take advantage of Cloudinary features like replacing backgrounds with generative AI and text overlays by adding additional props: 69 | 70 | 71 | 112 | 113 | 114 | 115 | ```jsx 116 | --- 117 | import { CldOgImage } from 'astro-cloudinary'; 118 | --- 119 | 158 | ``` 159 | 160 | 161 | [Check out more examples](/cldimage/examples) of what you can do with transformations in the CldImage docs because the CldOgImage supports all the params that the CldImage component does! 162 | 163 | ## Learn More about CldOgImage with the CldImage docs 164 | * [Configuration](/cldimage/configuration) 165 | * [Examples](/cldimage/examples) 166 | \ -------------------------------------------------------------------------------- /docs/src/content/docs/cldogimage/configuration.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Configuring CldOgImage 3 | description: Options and configuration for CldOgImage in Astro. 4 | head: 5 | - tag: title 6 | content: CldOgImage Configuration - Astro Cloudinary 7 | --- 8 | import CodeBlock from '../../../components/CodeBlock.astro'; 9 | import HeaderImage from '../../../components/HeaderImage.astro'; 10 | import Table from '../../../components/Table.astro'; 11 | 12 | The CldOgImage component provides a wide range of options for being able to easily optimize and transform images. 13 | 14 | {/* :::tip 15 | Configuration for CldOgImage is the same as [getCldImageUrl](/getcldimageurl/configuration), which both use the same underlying API. 16 | ::: */} 17 | 18 | ## Required Props 19 | 20 | The basic props required to use CldOgImage include: 21 | 22 |
(Dog catching a frisbee), 50 | }, 51 | { 52 | prop: 'twitterTitle', 53 | type: 'string', 54 | required: "Yes", 55 | example: () => (Frisbee Shop), 56 | }, 57 | { 58 | prop: 'src', 59 | type: 'string', 60 | required: 'Yes', 61 | example: () => (my-public-id) 62 | } 63 | ]} 64 | /> 65 | 66 | Beyond that all the extra props that the CldImage component supports can also be applied to the CldOgImage. Take a look at the CldImage [configuration](/cldimage/configuration) for the rest of the properties. -------------------------------------------------------------------------------- /docs/src/content/docs/clduploadwidget/basic-usage.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started with CldUploadWidget 3 | description: Upload images, videos, and other files in Astro with the CldUploadWidget component. 4 | ogImageTitle: CldUploadWidget 5 | head: 6 | - tag: title 7 | content: CldUploadWidget - Astro Cloudinary 8 | --- 9 | import { CldUploadWidget } from '../../../../../astro-cloudinary'; 10 | 11 | import CodeBlock from '../../../components/CodeBlock.astro'; 12 | import HeaderImage from '../../../components/HeaderImage.astro'; 13 | import Button from '../../../components/Button.astro'; 14 | import DemoUploadWidgetUnsigned from '../../../components/DemoUploadWidgetUnsigned.astro'; 15 | import DemoUploadWidgetSigned from '../../../components/DemoUploadWidgetSigned.astro'; 16 | 17 | The CldUploadWidget creates a new instance of the [Cloudinary Upload Widget](https://cloudinary.com/documentation/upload_widget) 18 | giving you an easy way to add upload capabilities to your Astro project. 19 | 20 | ## Basic Usage 21 | 22 | The CldUploadWidget will not render any UI by default. It will instead only render what's passed through in a ``. 23 | 24 | By default, the widget looks for any ` 51 | 52 | 53 | :::note 54 | All files uploaded to this demo will be deleted. 55 | ::: 56 | 57 | 58 | ```jsx 59 | --- 60 | import { CldUploadWidget } from 'astro-cloudinary'; 61 | --- 62 | 63 | 64 | 65 | ``` 66 | 67 | 68 | ### Signed 69 | 70 | Signing upload requests requires passing in an endpoint to the component. 71 | 72 | You can do this by creating a serverless function that reads the parameters as the body and returns an object with the signature. 73 | 74 | Use the following to generate an signed upload widget: 75 | 76 | 77 | 78 | 79 | 80 | :::note 81 | All files uploaded to this demo will be deleted. 82 | ::: 83 | 84 | 85 | ```jsx 86 | --- 87 | import { CldUploadWidget } from 'astro-cloudinary'; 88 | --- 89 | 90 | 91 | 92 | ``` 93 | 94 | 95 | Here's an example of how you could process the signature in an API endpoint that signs a request: 96 | 97 | 98 | ```js 99 | import type { APIRoute } from "astro"; 100 | import { v2 as cloudinary } from "cloudinary"; 101 | 102 | cloudinary.config({ 103 | cloud_name: import.meta.env.PUBLIC_CLOUDINARY_CLOUD_NAME, 104 | api_key: import.meta.env.PUBLIC_CLOUDINARY_API_KEY, 105 | api_secret: import.meta.env.CLOUDINARY_API_SECRET, 106 | }); 107 | 108 | export const prerender = false; 109 | 110 | export const POST: APIRoute = async ({ request }) => { 111 | const body = await request.json(); 112 | const { paramsToSign } = body; 113 | const signature = cloudinary.utils.api_sign_request(paramsToSign, import.meta.env.CLOUDINARY_API_SECRET); 114 | return Response.json({ signature }); 115 | } 116 | ``` 117 | 118 | 119 | To use the above, create a Node-based API route, add the snippet, and use that endpoint as the `signatureEndpoint` prop. 120 | 121 | [Learn more about signing uploads](/clduploadwidget/signed-uploads). 122 | 123 | Or see a full example of an API endpoint used with the Astro Cloudinary docs: https://github.com/cloudinary-community/astro-cloudinary/ 124 | 125 | ## Upload Events 126 | 127 | The Cloudinary Upload Widget emits a series of events throughout the upload lifecycle. Some of those 128 | events include adding an asset, starting the upload queue (in the event of multiple uploads), and 129 | upon successful upload. 130 | 131 | You can add callbacks for these events by adding an event listener to widget instance based on an 132 | associated ID. 133 | 134 | For instance, to listen for the successful upload of an asset, you can add: 135 | 136 | 137 | 138 | 139 | 140 | 141 | ```jsx 142 | --- 143 | import { CldUploadWidget } from 'astro-cloudinary'; 144 | --- 145 | 149 | 150 | 164 | ``` 165 | 166 | 167 | :::tip 168 | See the web console for an example event. 169 | ::: 170 | 171 | To see a complete list of events and the corresponding shape of results, 172 | visit the [Cloudinary Upload Widget API Reference](https://cloudinary.com/documentation/upload_widget_reference#events). 173 | 174 | The events are triggered with a pattern of `clduploadwidget:`. 175 | 176 | All events include a reference to the Upload Widget instance, which exposes instance 177 | methods such as `open` and `close`. 178 | 179 | 180 | ```jsx 181 | widget.addEventListener('clduploadwidget:queues-end', ((e: CustomEvent<{ detail: { UploadWidget: object } }>) => { 182 | e.detail.UploadWidget.close(); 183 | }); 184 | ``` 185 | 186 | 187 | To see a full list of instance methods, visit the [Cloudinary Upload Widget API Reference](https://cloudinary.com/documentation/upload_widget_reference#instance_methods). 188 | 189 | ## Learn More about CldUploadWidget 190 | * [Configuration](/clduploadwidget/configuration) 191 | * [Examples](/clduploadwidget/examples) 192 | -------------------------------------------------------------------------------- /docs/src/content/docs/clduploadwidget/configuration.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Configuring CldUploadWidget 3 | description: Options and configuration for CldUploadWidget in Astro. 4 | head: 5 | - tag: title 6 | content: CldUploadWidget Configuration - Astro Cloudinary 7 | --- 8 | import { CldUploadWidget } from '../../../../../astro-cloudinary'; 9 | 10 | import CodeBlock from '../../../components/CodeBlock.astro'; 11 | import HeaderImage from '../../../components/HeaderImage.astro'; 12 | import Table from '../../../components/Table.astro'; 13 | 14 | ## Basic Props 15 | 16 |
({`/api/sign-cloudinary-params.js`}), 39 | more: () => (More Info) 40 | }, 41 | { 42 | prop: 'uploadPreset', 43 | type: 'string', 44 | example: () => ({`my-upload-preset`}), 45 | more: () => (More Info) 46 | }, 47 | ]} 48 | /> 49 | 50 | ### `signatureEndpoint` 51 | 52 | An API endpoint used to sign the parameters generated during upload. 53 | 54 | ```jsx copy showLineNumbers 55 | signatureEndpoint="/api/sign-cloudinary-params" 56 | ``` 57 | 58 | Find an example of generating a signature on the [Signed Uploads page](/clduploadwidget/signed-uploads). 59 | 60 | [Or learn more about generating signatures](https://cloudinary.com/documentation/upload_images#generating_authentication_signatures) on the Cloudinary docs. 61 | 62 | ### `uploadPreset` 63 | 64 | ```jsx copy showLineNumbers 65 | uploadPreset="my-upload-preset" 66 | ``` 67 | 68 | [Learn more about upload presets](https://cloudinary.com/documentation/upload_presets) on the Cloudinary docs. 69 | 70 | ## Events 71 | 72 | Upload Widget events allow you to tap into different points of the upload lifecycle including 73 | when an upload has completed, but also when it starts the queue and more. 74 | 75 | CldUploadWidget supports all native events emitted from the Upload Widget the component wraps. 76 | The full list of events can be found on the [Cloudinary Upload Widget API reference](https://cloudinary.com/documentation/upload_widget_reference). 77 | 78 | In order to trigger a function based on an event, an event listener can be attached to the widget. 79 | 80 | 81 | ```jsx 82 | --- 83 | import { CldUploadWidget } from 'astro-cloudinary'; 84 | --- 85 | 89 | 90 | 99 | ``` 100 | 101 | 102 | All events follow the same pattern: 103 | 104 | 105 | ``` 106 | clduploadwidget: 107 | ``` 108 | 109 | 110 | Where `` is based on the name of the event found in the Cloudinary documentation. 111 | 112 | The Custom Event will include a details property which allows for access of the event information 113 | including results, such as the uploaded resource when listening to the success event. 114 | 115 | 116 |
149 | 150 | 151 | ## Advanced 152 | 153 | ### Configuration 154 | 155 |
({`{ url: { cloudName: 'spacejelly' } }`}), 183 | more: () => (More Info) 184 | }, 185 | { 186 | prop: 'options', 187 | type: 'object', 188 | example: () => ({`{encryption: {...}}`}), 189 | more: () => (More Info) 190 | }, 191 | ]} 192 | /> 193 | 194 | #### `config` 195 | 196 | Allows configuration for the Cloudinary environment. 197 | 198 | **Examples** 199 | 200 | ```jsx copy 201 | config={{ 202 | cloud: { 203 | cloudName: '', 204 | apiKey: '' 205 | } 206 | }} 207 | ``` 208 | 209 | #### `options` 210 | 211 | Parameters used to customize and configure the Upload Widget instance. These options are passed in 212 | directly to the Upload Widget, exposing all available parameters through the `options` object. 213 | 214 | ```jsx copy showLineNumbers 215 | options={{ 216 | sources: ['local', 'url', 'unsplash'], 217 | multiple: true, 218 | maxFiles: 5 219 | }} 220 | ``` 221 | 222 | [Learn more about Upload Widget parameters](https://cloudinary.com/documentation/upload_widget_reference#parameters) on the Cloudinary docs. -------------------------------------------------------------------------------- /docs/src/content/docs/clduploadwidget/examples.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: CldUploadWidget Examples 3 | description: Examples of how to upload images, videos, and other files in Astro with the CldUploadWidget component. 4 | head: 5 | - tag: title 6 | content: CldUploadWidget Examples - Astro Cloudinary 7 | --- 8 | import { CldUploadWidget } from '../../../../../astro-cloudinary'; 9 | 10 | import CodeBlock from '../../../components/CodeBlock.astro'; 11 | import HeaderImage from '../../../components/HeaderImage.astro'; 12 | import Button from '../../../components/Button.astro'; 13 | import DemoUploadWidgetUnsigned from '../../../components/DemoUploadWidgetUnsigned.astro'; 14 | import DemoUploadWidgetSigned from '../../../components/DemoUploadWidgetSigned.astro'; 15 | 16 | ## Signed Uploads 17 | 18 | 19 | 20 | 21 | 22 | :::note 23 | All files uploaded to this demo will be deleted. 24 | ::: 25 | 26 | 27 | ```jsx 28 | --- 29 | import { CldUploadWidget } from 'astro-cloudinary'; 30 | --- 31 | 32 | 33 | 34 | ``` 35 | 36 | 37 | ## Sources 38 | 39 | Controls which sources files can be selected from. 40 | 41 | 46 | 47 | 48 | 49 | :::note 50 | All files uploaded to this demo will be deleted. 51 | ::: 52 | 53 | 54 | ```jsx 55 | --- 56 | import { CldUploadWidget } from 'astro-cloudinary'; 57 | --- 58 | 59 | 60 | 61 | ``` 62 | 63 | 64 | ## Unsigned Uploads 65 | 66 | 67 | 68 | 69 | 70 | :::note 71 | All files uploaded to this demo will be deleted. 72 | ::: 73 | 74 | 75 | ```jsx 76 | --- 77 | import { CldUploadWidget } from 'astro-cloudinary'; 78 | --- 79 | 80 | 81 | 82 | ``` 83 | -------------------------------------------------------------------------------- /docs/src/content/docs/clduploadwidget/signed-uploads.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Signing Uploads with CldUploadWidget 3 | description: How to make signed uploads in Astro with the CldUploadWidget component. 4 | head: 5 | - tag: title 6 | content: CldUploadWidget Signed Uploads - Astro Cloudinary 7 | --- 8 | import { CldUploadWidget } from '../../../../../astro-cloudinary'; 9 | 10 | import CodeBlock from '../../../components/CodeBlock.astro'; 11 | import HeaderImage from '../../../components/HeaderImage.astro'; 12 | import Button from '../../../components/Button.astro'; 13 | import DemoUploadWidgetSigned from '../../../components/DemoUploadWidgetSigned.astro'; 14 | 15 | Signing requests is an easy way to provide enhanced security for your file uploads. This helps 16 | deter people from making unauthenticated uploads to your cloud. 17 | 18 | ## Configuring Signed Uploads 19 | 20 | Setting up CldUploadWidget to sign uploads is as simple as passing an API endpoint as a prop: 21 | 22 | 23 | ```jsx 24 | --- 25 | import { CldUploadWidget } from 'astro-cloudinary'; 26 | --- 27 | 28 | 29 | 30 | ``` 31 | 32 | 33 | However, that API endpoint needs to return a specific shape in order to properly communicate 34 | with the CldUploadWidget. 35 | 36 | ## API Endpoint to Sign Requests 37 | 38 | When working in Astro, we gain access to a server environment through both somewhat traditional means 39 | and serverless functions. 40 | 41 | This means, we can use the [Cloudinary Node SDK](https://cloudinary.com/documentation/node_integration) 42 | in order to easily sign our requests. 43 | 44 | To start off, install the Cloudinary Node SDK with: 45 | 46 | 47 | ``` 48 | npm install cloudinary 49 | ``` 50 | 51 | 52 | In order to sign our requests, we'll need to use our Cloudinary API Key and Secret, so set additional 53 | environment variables: 54 | 55 | 56 | ``` 57 | PUBLIC_CLOUDINARY_API_KEY="" 58 | CLOUDINARY_API_SECRET="" 59 | ``` 60 | 61 | 62 | Then, we want to create a new API endpoint: 63 | 64 | Create a new API route file such as `src/pages/api/sign-cloudinary-params.ts` and inside add the following: 65 | 66 | 67 | ```js 68 | import type { APIRoute } from "astro"; 69 | import { v2 as cloudinary } from "cloudinary"; 70 | 71 | cloudinary.config({ 72 | cloud_name: import.meta.env.PUBLIC_CLOUDINARY_CLOUD_NAME, 73 | api_key: import.meta.env.PUBLIC_CLOUDINARY_API_KEY, 74 | api_secret: import.meta.env.CLOUDINARY_API_SECRET, 75 | }); 76 | 77 | export const prerender = false; 78 | 79 | export const POST: APIRoute = async ({ request }) => { 80 | const body = await request.json(); 81 | const { paramsToSign } = body; 82 | const signature = cloudinary.utils.api_sign_request(paramsToSign, import.meta.env.CLOUDINARY_API_SECRET); 83 | return Response.json({ signature }); 84 | } 85 | ``` 86 | 87 | 88 | Whatever path you use will now be the value of `signatureEndpoint` for the CldUploadWidget. -------------------------------------------------------------------------------- /docs/src/content/docs/cldvideoplayer/basic-usage.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started with CldVideoPlayer 3 | description: Optimize and transform videos in Astro with the CldVideoPlayer component. 4 | ogImageTitle: CldVideoPlayer 5 | head: 6 | - tag: title 7 | content: CldVideoPlayer - Astro Cloudinary 8 | --- 9 | import { CldVideoPlayer } from '../../../../../astro-cloudinary'; 10 | 11 | import CodeBlock from '../../../components/CodeBlock.astro'; 12 | import HeaderImage from '../../../components/HeaderImage.astro'; 13 | import DemoVideoPlayerEvents from '../../../components/DemoVideoPlayerEvents.astro'; 14 | 15 | The CldVideoPlayer component helps to embed Cloudinary videos using 16 | the [Cloudinary Video Player](https://cloudinary.com/documentation/cloudinary_video_player) giving 17 | you a full customizable experience for your player. 18 | 19 | ## Basic Usage 20 | 21 | To add a video player, use the CldVideoPlayer component with the basic required props 22 | `width`, `height`, and `src`. 23 | 24 | 25 | 30 | 31 | 32 | 33 | ```jsx 34 | --- 35 | import { CldVideoPlayer } from 'astro-cloudinary'; 36 | --- 37 | 42 | ``` 43 | 44 | 45 | ## Customization 46 | 47 | You can further take advantage of features like customizing your player: 48 | 49 | 50 | 61 | 62 | 63 | 64 | ```jsx 65 | --- 66 | import { CldVideoPlayer } from 'astro-cloudinary'; 67 | --- 68 | 79 | ``` 80 | 81 | 82 | ## Player Events 83 | 84 | For listening to player events for advanced interactions with: 85 | 86 | 87 | 92 | 93 | 94 | 95 | ```jsx 96 | --- 97 | import { CldVideoPlayer } from 'astro-cloudinary'; 98 | --- 99 | 105 | 106 | 123 | ``` 124 | 125 | 126 | :::tip 127 | See your browser's web console to preview the information logged in the above callbacks. 128 | ::: 129 | 130 | ## Learn More about CldVideoPlayer 131 | * [Configuration](/cldvideoplayer/configuration) 132 | * [Examples](/cldvideoplayer/examples) -------------------------------------------------------------------------------- /docs/src/content/docs/getcldimageurl/basic-usage.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started with getCldImageUrl 3 | description: Optimize and transform images in Astro with the getCldImageUrl component. 4 | ogImageTitle: getCldImageUrl 5 | head: 6 | - tag: title 7 | content: getCldImageUrl - Astro Cloudinary 8 | --- 9 | import { CldImage } from '../../../../../astro-cloudinary'; 10 | import { getCldImageUrl } from '../../../../../astro-cloudinary/helpers'; 11 | 12 | import CodeBlock from '../../../components/CodeBlock.astro'; 13 | import HeaderImage from '../../../components/HeaderImage.astro'; 14 | import DemoImageEvents from '../../../components/DemoImageEvents.astro'; 15 | 16 | The getCldImageUrl helper provides an easy way to generate image URLs from Cloudinary in an Astro app. 17 | 18 | With it comes access to more advanced features like dynamic cropping, background removal, overlays, and other Cloudinary transformations. 19 | 20 | getCldImageUrl is the API the powers [CldImage](/cldimage/basic-usage), so usage works the same. 21 | 22 | ## Basic Usage 23 | 24 | The basic required props include `width`, `height`, `src`, and `alt`: 25 | 26 | 27 | 37 | 38 | 39 | 40 | ```jsx 41 | import { getCldImageUrl } from 'astro-cloudinary/helpers'; 42 | 43 | const url = getCldImageUrl({ src: '' }); 44 | ``` 45 | 46 | 47 | The `src` property takes in a Cloudinary Public ID which includes the folder path along with the ID of the image itself. 48 | The `width` and the `height` should represent the rendered size and the `alt` value should be a text-based description 49 | of the image. 50 | 51 | The `sizes` prop is optional, but recommended for [Responsive Sizing](/guides/responsive-images). 52 | 53 | ## Transformations 54 | 55 | You can further take advantage of Cloudinary features like replacing backgrounds with generative AI and text overlays by adding additional props: 56 | 57 | 58 | 74 | 75 | 76 | 77 | ```jsx 78 | import { getCldImageUrl } from 'astro-cloudinary/helpers'; 79 | 80 | const url = getCldImageUrl({ 81 | src: '' 82 | crop: { 83 | type: 'fill', 84 | source: true 85 | }, 86 | replaceBackground: 'space', 87 | }); 88 | ``` 89 | 90 | 91 | [Check out more examples](/getcldimageurl/examples) of what you can do with transformations! 92 | 93 | ## Using Cloudinary URLs 94 | 95 | CldImage supports passing a fully qualified Cloudinary URL as the `src`, however, it 96 | must include a version number (`/v1234/`) in order to be correctly parsed. 97 | 98 | :::note 99 | The version number is required due to the variable nature of Cloudinary URLs. This helps 100 | to ensure the integretiy when during the parsing process. 101 | ::: 102 | 103 | 104 | ```jsx 105 | import { getCldImageUrl } from 'astro-cloudinary/helpers'; 106 | 107 | const url = getCldImageUrl({ 108 | src: 'https://res.cloudinary.com/mycloud/image/upload/v1234/myimage', 109 | }); 110 | ``` 111 | 112 | 113 | ### Preserving URL Transformations 114 | 115 | When passing the result of getCldImageUrl to the CldImage component, it's important 116 | to preserve any transformations, as by default, CldImage will only maintain the 117 | public ID of the URL provided. 118 | 119 | To preserve those transformations, you can apply the `preserveTransformations` property 120 | to CldImage: 121 | 122 | 123 | ```jsx 124 | import { getCldImageUrl } from 'astro-cloudinary/helpers'; 125 | 126 | const url = getCldImageUrl({ 127 | src: 'https://res.cloudinary.com//image/upload/w_100,h_200,c_fill/v1234/myimage', 128 | preserveTransformations: true 129 | }); 130 | ``` 131 | 132 | 133 | For example: 134 | 135 | 136 | 145 | 146 | 147 | 148 | ```jsx 149 | import { getCldImageUrl } from 'astro-cloudinary/helpers'; 150 | 151 | const url = getCldImageUrl({ 152 | src: 'https://res.cloudinary.com//image/upload/e_background_removal/b_blueviolet/f_auto/q_auto/v1/cld-sample-5', 153 | preserveTransformations: true 154 | }) 155 | ``` 156 | 157 | 158 | Would generate a URL of: 159 | 160 | 161 | ``` 162 | https://res.cloudinary.com//image/upload/e_background_removal/b_blueviolet/f_auto/q_auto/c_limit,w_1600/v1/cld-sample-5?_a=BBGAABS00 163 | ``` 164 | 165 | 166 | ## Learn More about getCldImageUrl 167 | 168 | * [Configuration](/getcldimageurl/configuration) 169 | * [Examples](/getcldimageurl/examples) 170 | -------------------------------------------------------------------------------- /docs/src/content/docs/getcldogimageurl/basic-usage.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started with getCldOgImageUrl 3 | description: Generate dynamic Social Media Card URLs in Astro with the getCldOgImageUrl helper. 4 | ogImageTitle: getCldOgImageUrl 5 | head: 6 | - tag: title 7 | content: getCldOgImageUrl - Astro Cloudinary 8 | --- 9 | import { CldImage } from '../../../../../astro-cloudinary'; 10 | import { getCldOgImageUrl } from '../../../../../astro-cloudinary/helpers'; 11 | 12 | import CodeBlock from '../../../components/CodeBlock.astro'; 13 | import HeaderImage from '../../../components/HeaderImage.astro'; 14 | import DemoImageEvents from '../../../components/DemoImageEvents.astro'; 15 | 16 | The getCldOgImageUrl function helps you easily generate Social Card image URLs (Open Graph Images) using Cloudinary. 17 | 18 | This URL can be then used when defining metadata for your page within Astro. 19 | 20 | ## Basic Usage 21 | 22 | getCldOgImageUrl makes it convenient to create Social Card URLs out-of-the-box. 23 | 24 | The only needed prop is `src`: 25 | 26 | 27 | 37 | 38 | 39 | > Note: The image above is rendered using the [CldImage component](/cldimage/basic-usage) for preview only. 40 | 41 | 42 | ```jsx 43 | import { getCldOgImageUrl } from 'astro-cloudinary/helpers'; 44 | 45 | const url = getCldOgImageUrl({ src: '' }); 46 | ``` 47 | 48 | 49 | The function simply returns a URL for the given image's public ID including default width and height. 50 | 51 | :::tip 52 | getCldOgImageUrl is a deritive of getCldImageUrl meaning it generally has the same API, but provides 53 | a few defaults for Open Graph images like sizes. 54 | ::: 55 | 56 | ## Adding Open Graph Meta Tags 57 | 58 | Adding social cards in Astro is as simple as adding Open Graph and Twitter meta tags in the `` of 59 | whatever page you'd like to include them on. 60 | 61 | The only depending factor is where you manage your tags, which can include a `Layout.astro` file, 62 | inside of a managed `Head.astro` component, or other solutions. 63 | 64 | To add your tags, generate your URL and apply the tags like the following: 65 | 66 | 67 | ```jsx 68 | --- 69 | import { getCldOgImageUrl } from 'astro-cloudinary/helpers'; 70 | 71 | const ogImageUrl = getCldOgImageUrl({ src: '' }); 72 | --- 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | ``` 81 | 82 | 83 | ## Image Size 84 | 85 | By default, the image canvas is based upon 1200x630. 630 is used to satisfy the 1.91:1 ratio requirement and minimum size 86 | [requirement from linkedin](https://www.linkedin.com/help/linkedin/answer/a521928/make-your-website-shareable-on-linkedin). 87 | 88 | You can use the `width` and the `height` to control the canvas size. 89 | 90 | ## Image Format 91 | 92 | While Cloudinary's `f_auto` parameter ([format of auto](https://cloudinary.com/documentation/image_optimization#how_to_optimize_image_format)) is great for websites and mobile apps, having more control over the format helps to reduce initial encoding time, which is more critical for a social network to recognize the image and load it on first share. 93 | 94 | The safe default format for most use cases is then jpg, as webp does not have broad support (likely nor does AVIF). 95 | 96 | > Read more about webp support: https://www.ctrl.blog/entry/webp-ogp.html 97 | 98 | If you have the control in your application to produce multiple image sources, such has having a 99 | separate `og:image` and `twitter:image`, you can generate two (or more) URLs to produce as optimized 100 | a format as you can for the platform: 101 | 102 | 103 | ```js 104 | --- 105 | import { getCldOgImageUrl } from 'astro-cloudinary'; 106 | 107 | // getCldOgImageUrl defaults to jpg 108 | 109 | const ogImageUrl = getCldOgImageUrl({ 110 | src: '', 111 | }); 112 | 113 | const twitterImageUrl = getCldOgImageUrl({ 114 | src: '', 115 | format: 'webp', 116 | }); 117 | --- 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | ``` 126 | 127 | 128 | Find out how else you can customize your Cloudinary image over on [getCldOgImageUrl configuration](/getcldogimageurl/configuration). 129 | 130 | ## Learn More about getCldOgImageUrl 131 | * [Configuration](/getcldogimageurl/configuration) 132 | * [Examples](/getcldogimageurl/examples) -------------------------------------------------------------------------------- /docs/src/content/docs/getcldogimageurl/configuration.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Configuring getCldOgImageUrl 3 | description: Options and configuration for getCldOgImageUrl in Astro. 4 | head: 5 | - tag: title 6 | content: getCldOgImageUrl Configuration - Astro Cloudinary 7 | --- 8 | import { CldImage } from '../../../../../astro-cloudinary'; 9 | 10 | import CodeBlock from '../../../components/CodeBlock.astro'; 11 | import HeaderImage from '../../../components/HeaderImage.astro'; 12 | import Table from '../../../components/Table.astro'; 13 | 14 | The getCldOgImageUrl helper provides a wide range of options for being able to easily generate social cards for Open Graph images. 15 | 16 | Configuration for getCldOgImageUrl is the same as [getCldImageUrl](/getcldimageurl/configuration). The only difference is getCldOgImageUrl 17 | provides the following default settings to make generating an OG image URL simpler. 18 | 19 | :::tip 20 | To see all available options, visit [getCldImageUrl](/getcldimageurl/configuration). 21 | ::: 22 | 23 | ## Required Props 24 | 25 |
(my-public-id) 53 | }, 54 | ]} 55 | /> 56 | 57 | ## Basic Transformations 58 | 59 |
({`{ type: 'fill', gravity: 'center', source: true }`}), 86 | example: () => (scale), 87 | more: () => (More Info) 88 | }, 89 | { 90 | prop: 'height', 91 | type: 'number', 92 | default: () => (630), 93 | example: () => (630), 94 | more: () => (More Info) 95 | }, 96 | 97 | { 98 | prop: 'width', 99 | type: 'number', 100 | default: () => (1200), 101 | example: () => (1200), 102 | more: () => (More Info) 103 | }, 104 | ]} 105 | /> 106 | 107 | Find more configuration settings over at [getCldImageUrl configuration](/getcldimageurl/configuration). -------------------------------------------------------------------------------- /docs/src/content/docs/getcldogimageurl/examples.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: getCldOgImageUrl Examples 3 | description: Examples of generating social media cards using getCldOgImageUrl in Astro with Cloudinary. 4 | head: 5 | - tag: title 6 | content: getCldOgImageUrl Examples - Astro Cloudinary 7 | --- 8 | import { CldImage } from '../../../../../astro-cloudinary'; 9 | import { getCldImageUrl } from '../../../../../astro-cloudinary/helpers'; 10 | 11 | import CodeBlock from '../../../components/CodeBlock.astro'; 12 | import HeaderImage from '../../../components/HeaderImage.astro'; 13 | import ExamplesCldOgImage from '../../../components/ExamplesCldOgImage.astro'; 14 | 15 | 16 | The below examples use the CldImage component to render the images. This is not required, you can use the URL returned by getCldOgImageUrl in any way you like. 17 | 18 | ## Basic Image 19 | 20 | `src`: The public ID of the image to use as a Social Card 21 | 22 | 23 | 26 | 27 | 28 | 29 | ```jsx 30 | import { getCldOgImageUrl } from 'astro-cloudinary/helpers'; 31 | 32 | const url = getCldOgImageUrl({ src: '' }); 33 | ``` 34 | 35 | 36 | ## Change Background 37 | 38 | `replaceBackground`: Uses generative AI to replace a background 39 | 40 | 41 | 45 | 46 | 47 | 48 | ```jsx 49 | import { getCldOgImageUrl } from 'astro-cloudinary/helpers'; 50 | 51 | const url = getCldOgImageUrl({ 52 | src: '', 53 | replaceBackground: '' 54 | }); 55 | ``` 56 | 57 | 58 | ## Text Overlay 59 | 60 | `text`: Adds basic text to the image 61 | 62 | 63 | 75 | 76 | 77 | 78 | ```jsx 79 | import { getCldOgImageUrl } from 'astro-cloudinary/helpers'; 80 | 81 | const url = getCldOgImageUrl({ 82 | src: '', 83 | overlays: [{ 84 | text: { 85 | fontFamily: 'Source Sans Pro', 86 | fontSize: 120, 87 | fontWeight: 'bold', 88 | text: 'Astro Cloudinary' 89 | } 90 | }] 91 | }); 92 | ``` 93 | -------------------------------------------------------------------------------- /docs/src/content/docs/getcldvideourl/basic-usage.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started with getCldVideoUrl 3 | description: Optimize and transform images in Astro with the getCldVideoUrl component. 4 | ogImageTitle: getCldVideoUrl 5 | head: 6 | - tag: title 7 | content: getCldVideoUrl - Astro Cloudinary 8 | --- 9 | import { CldImage } from '../../../../../astro-cloudinary'; 10 | import { getCldVideoUrl } from '../../../../../astro-cloudinary/helpers'; 11 | 12 | import CodeBlock from '../../../components/CodeBlock.astro'; 13 | import HeaderImage from '../../../components/HeaderImage.astro'; 14 | import DemoImageEvents from '../../../components/DemoImageEvents.astro'; 15 | 16 | You can use the getCldVideoUrl helper function to generate Cloudinary URLs without the component wrapper. 17 | 18 | getCldOgImageUrl is a deritive of [getCldImageUrl](/getcldimageurl/basic-usage) meaning it generally has the same API, but provides a few defaults for videos in particular, setting the asset type to video and doesn't make available image-specific transformations. -------------------------------------------------------------------------------- /docs/src/content/docs/getcldvideourl/configuration.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Configuring getCldVideoUrl 3 | description: Options and configuration for getCldVideoUrl in Astro. 4 | head: 5 | - tag: title 6 | content: getCldVideoUrl Configuration - Astro Cloudinary 7 | --- 8 | import { CldImage } from '../../../../../astro-cloudinary'; 9 | 10 | import CodeBlock from '../../../components/CodeBlock.astro'; 11 | import HeaderImage from '../../../components/HeaderImage.astro'; 12 | import Table from '../../../components/Table.astro'; 13 | 14 | Configuration for getCldVideoUrl is the same as [getCldImageUrl](/getcldimageurl/configuration). 15 | 16 | The only difference is getCldVideoUrl provides the following default settings: 17 | 18 |
41 | 42 | Additionally, the transformations do not have access to image-specific transformations. 43 | 44 | Find more configuration settings over at [getCldImageUrl configuration](/getcldimageurl/configuration). -------------------------------------------------------------------------------- /docs/src/content/docs/getcldvideourl/examples.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: getCldVideoUrl Examples 3 | description: Examples of Cloudinary video URLs in Astro with Cloudinary. 4 | head: 5 | - tag: title 6 | content: getCldVideoUrl Examples - Astro Cloudinary 7 | --- 8 | import { getCldVideoUrl, getCldImageUrl } from "../../../../../astro-cloudinary/helpers"; 9 | 10 | import CodeBlock from "../../../components/CodeBlock.astro"; 11 | import ImageGrid from "../../../components/ImageGrid.astro"; 12 | import HeaderImage from "../../../components/HeaderImage.astro"; 13 | 14 | ## Basic Usage 15 | 16 | Standard Cloudinary Video Player with playback. 17 | 18 | 19 | 31 | 32 | 33 | 34 | ```jsx 35 | --- 36 | import { getCldVideoUrl } from 'astro-cloudinary/helpers'; 37 | --- 38 | const url = getCldVideoUrl({ 39 | src: "" 40 | }) 41 | ``` 42 | 43 | 44 | ## Transformations 45 | 46 | Cloudinary provides the following transformations along with the getCldVideoUrl function. 47 | 48 | ### Cropping & Resizing 49 | 50 | Crop and resize the video to fit the container. 51 | 52 | 53 | 67 | 68 | 69 | ```jsx 70 | --- 71 | import { getCldVideoUrl } from 'astro-cloudinary'; 72 | --- 73 | const url = getCldVideoUrl({ 74 | src: "", 75 | width: 500, 76 | height: 500, 77 | crop: 'fill', 78 | gravity: 'auto' 79 | }) 80 | ``` 81 | 82 | 83 | ### Text Overlays 84 | 85 | Place text over the video (Ex: subtitles). 86 | 87 | 88 | 130 | 131 | 132 | ```jsx 133 | --- 134 | import { getCldVideoUrl } from 'astro-cloudinary'; 135 | --- 136 | const url = getCldVideoUrl({ 137 | src: "", 138 | overlays: [ 139 | { 140 | position: { 141 | x: 150, 142 | y: 125, 143 | angle: -20, 144 | gravity: 'south_east', 145 | }, 146 | text: { 147 | color: 'magenta', 148 | fontFamily: 'Source Sans Pro', 149 | fontSize: 150, 150 | fontWeight: 'black', 151 | text: 'WITH STYLE' 152 | } 153 | }, 154 | { 155 | position: { 156 | x: 140, 157 | y: 140, 158 | angle: -20, 159 | gravity: 'south_east', 160 | }, 161 | text: { 162 | color: 'white', 163 | fontFamily: 'Source Sans Pro', 164 | fontSize: 150, 165 | fontWeight: 'black', 166 | text: 'WITH STYLE' 167 | } 168 | }, 169 | ] 170 | }) 171 | ``` 172 | 173 | 174 | ### Image Overlays 175 | 176 | Place an image over the video (Ex: watermarks). 177 | 178 | 179 | 201 | 202 | 203 | ```jsx 204 | --- 205 | import { getCldVideoUrl } from 'astro-cloudinary'; 206 | --- 207 | const url = getCldVideoUrl({ 208 | src: "", 209 | overlays: [ 210 | { 211 | position: { 212 | x: 40, 213 | y: 40, 214 | gravity: 'north_east', 215 | }, 216 | publicId: "", 217 | width: 150, 218 | height: 150 219 | } 220 | ] 221 | }) 222 | ``` 223 | -------------------------------------------------------------------------------- /docs/src/content/docs/guides/background-removal.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Remove a Background from an Image 3 | description: Remove a Background from an Image - Astro Cloudinary 4 | head: 5 | - tag: title 6 | content: Background Removal - Astro Cloudinary 7 | --- 8 | 9 | import { Tabs, TabItem } from '@astrojs/starlight/components'; 10 | import { CldImage } from '../../../../../astro-cloudinary'; 11 | import { getCldImageUrl } from '../../../../../astro-cloudinary/helpers'; 12 | import CodeBlock from '../../../components/CodeBlock.astro'; 13 | import HeaderImage from '../../../components/HeaderImage.astro'; 14 | 15 | Astro Cloudinary supports background removal and modification. 16 | 17 | The CldImage component removes backgrounds from images using the `removeBackground` prop. 18 | 19 | :::tip 20 | Removing backgrounds requires enabling the [Cloudinary AI Background Removal Add-On](https://cloudinary.com/documentation/cloudinary_ai_background_removal_addon), which includes a free tier for getting started. 21 | ::: 22 | 23 | ## Examples 24 | 25 | ### Remove Background 26 | 27 | 28 | 37 | 48 | 49 | 50 | 51 | 52 | 53 | ```jsx copy 54 | import { CldImage } from 'astro-cloudinary'; 55 | 56 | 64 | ``` 65 | 66 | 67 | 68 | 69 | ```jsx copy 70 | import { getCldImageUrl } from 'astro-cloudinary'; 71 | 72 | getCldImageUrl({ 73 | src: '', 74 | removeBackground: true 75 | }) 76 | ``` 77 | 78 | 79 | 80 | 81 | ### Color Change 82 | 83 | Use `background` to specify a background color. 84 | 85 | 86 | 95 | 107 | 108 | 109 | 110 | ```jsx copy 111 | import { getCldImageUrl } from 'astro-cloudinary'; 112 | 113 | getCldImageUrl({ 114 | src: "", 115 | removeBackground: true, 116 | background: "blueviolet" 117 | }); 118 | ``` 119 | 120 | 121 | ### Replace with Image 122 | 123 | Use `underlay: ""` to specify an image to use as a background. 124 | 125 | 126 | 135 | 147 | 148 | 149 | 150 | ```jsx copy 151 | import { getCldImageUrl } from 'astro-cloudinary'; 152 | 153 | getCldImageUrl({ 154 | src: "", 155 | removeBackground: true, 156 | underlay: "" 157 | }); 158 | ``` 159 | 160 | 161 | ### AI-Generated Background 162 | 163 | Use `replaceBackground=""` for an AI-generated prompt-based background. 164 | 165 | :::note 166 | The generative replace background transformation is currently in Beta. [Learn more](https://cloudinary.com/documentation/transformation_reference#e_gen_background_replace). 167 | ::: 168 | 169 | 170 | 180 | 191 | 192 | 193 | 194 | ```jsx copy 195 | import { CldImage } from 'astro-cloudinary'; 196 | 197 | 206 | ``` 207 | 208 | 209 | ## Learn More 210 | * [CldImage](/cldimage/basic-usage) 211 | * [getCldImageUrl](/getcldimageurl/basic-usage) 212 | -------------------------------------------------------------------------------- /docs/src/content/docs/guides/image-optimization.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Optimizing Images in Astro 3 | description: Boost performance by optimizing images in Astro with Cloudinary. 4 | head: 5 | - tag: title 6 | content: Image Optimization - Astro Cloudinary 7 | --- 8 | import { Tabs, TabItem } from '@astrojs/starlight/components'; 9 | 10 | import { CldImage } from '../../../../../astro-cloudinary'; 11 | 12 | import CodeBlock from '../../../components/CodeBlock.astro'; 13 | import HeaderImage from '../../../components/HeaderImage.astro'; 14 | 15 | Optimization plays a critical role in making your website load as fast as possible. 16 | 17 | With the CldImage component, your images will be automatically optimized with automatic 18 | compression and by delivering the most efficient format based on your browser or device 19 | (WebP, AVIF, etc). 20 | 21 | :::tip 22 | You can further optimize delivery with [responsive sizing](/guides/responsive-images) by using the `sizes` prop. 23 | ::: 24 | 25 | ## Example 26 | 27 | 28 | 35 | 36 | 37 | 38 | 39 | 40 | ```jsx copy 41 | import { CldImage } from 'astro-cloudinary'; 42 | 43 | 50 | ``` 51 | 52 | 53 | 54 | 55 | ```jsx copy 56 | import { getCldImageUrl } from 'astro-cloudinary/helpers'; 57 | 58 | getCldImageUrl({ 59 | width: 960, 60 | height: 600, 61 | src: '' 62 | }); 63 | ``` 64 | 65 | 66 | 67 | 68 | ## Learn More 69 | * [Responsive Images](/guides/responsive-images) 70 | * [CldImage](/cldimage/basic-usage) 71 | * [getCldImageUrl](/getcldimageurl/basic-usage) -------------------------------------------------------------------------------- /docs/src/content/docs/guides/image-overlays.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Image Overlays 3 | description: Placing images on top of images in Astro with Cloudinary. 4 | head: 5 | - tag: title 6 | content: Image Overlays - Astro Cloudinary 7 | --- 8 | import { Tabs, TabItem } from '@astrojs/starlight/components'; 9 | 10 | import { CldImage } from '../../../../../astro-cloudinary'; 11 | 12 | import CodeBlock from '../../../components/CodeBlock.astro'; 13 | import HeaderImage from '../../../components/HeaderImage.astro'; 14 | 15 | With the CldImage component, you can easily place an image onto an existing 16 | image with overlays. 17 | 18 | ## Example 19 | 20 | 21 | 44 | 45 | 46 | 47 | 48 | 49 | ```jsx 50 | import { CldImage } from 'astro-cloudinary'; 51 | 52 | ", 58 | position: { 59 | x: 50, 60 | y: 50, 61 | gravity: 'north_east', 62 | }, 63 | effects: [{ 64 | crop: 'fill', 65 | width: 350, 66 | height: 350, 67 | gravity: 'auto', 68 | border: '5px_solid_yellow' 69 | }] 70 | }]} 71 | alt="" 72 | sizes="100vw" 73 | /> 74 | ``` 75 | 76 | 77 | 78 | 79 | ```jsx 80 | import { getCldImageUrl } from 'astro-cloudinary/helpers'; 81 | 82 | getCldImageUrl({ 83 | width: 1335, 84 | height: 891, 85 | src: '', 86 | overlays: [{ 87 | publicId: '', 88 | position: { 89 | x: 50, 90 | y: 50, 91 | gravity: 'north_east', 92 | }, 93 | effects: [{ 94 | crop: 'fill', 95 | width: 350, 96 | height: 350, 97 | gravity: 'auto', 98 | border: '5px_solid_yellow' 99 | }] 100 | }] 101 | }) 102 | ``` 103 | 104 | 105 | 106 | 107 | ## Learn More 108 | * [CldImage](/cldimage/basic-usage) 109 | * [getCldImageUrl](/getcldimageurl/basic-usage) -------------------------------------------------------------------------------- /docs/src/content/docs/guides/social-media-card.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Generating Social Media Cards 3 | description: Boosting social engagement with custom Social Media Cards in Astro. 4 | head: 5 | - tag: title 6 | content: Social Media Cards - Astro Cloudinary 7 | --- 8 | import { Tabs, TabItem } from '@astrojs/starlight/components'; 9 | 10 | import { CldImage } from '../../../../../astro-cloudinary'; 11 | 12 | import CodeBlock from '../../../components/CodeBlock.astro'; 13 | import HeaderImage from '../../../components/HeaderImage.astro'; 14 | 15 | Social Media Cards are critical for boosting engagement when sharing links 16 | on social media websites like Twiter/X, LinkedIn, and Facebook. 17 | 18 | They work by utilizing the the Open Graph standard with HTML meta tags 19 | like `og:image` to define the image you would like to show as a preview 20 | when your link is shared. 21 | 22 | You can use Astro Cloudinary to easily generate dynamic social media cards 23 | for your Astro app using the [getCldOgImageUrl](/getcldogimageurl/basic-usage) helper. 24 | 25 | ## Example 26 | 27 | Using getCldOgImageUrl, you can first generate the URL of the image you want to 28 | create. This can be as simple as a reference to an image in your Cloudinary account 29 | or can be a dynamically generated image with text overlays, image overlays, or any 30 | effect supported by the Trasnformation API. 31 | 32 | For instance, if you have a blog, you may one unique Social Cards for each page, 33 | where you can apply a text overlay based on that blog's title. 34 | 35 | 36 | 128 | 129 | 130 | 131 | 132 | 133 | ```jsx copy showLineNumbers 134 | import { getCldOgImageUrl } from 'astro-cloudinary/helpers'; 135 | 136 | const publicId = 'samples/balloons'; 137 | const headline = 'High-Performance Image & Video Delivery at Scale in Astro'; 138 | const tagline = 'Astro Cloudinary'; 139 | const logoPublicId = 'samples/logo'; 140 | 141 | const url = getCldOgImageUrl({ 142 | src: publicId, 143 | effects: [ 144 | { 145 | background: 'rgb:010A44' 146 | }, 147 | { 148 | color: 'rgb:2A005F', 149 | colorize: '100' 150 | }, 151 | { 152 | gradientFade: 'symmetric' 153 | } 154 | ], 155 | overlays: [ 156 | { 157 | publicId, 158 | width: 1200, 159 | height: 630, 160 | crop: 'fill', 161 | effects: [ 162 | { opacity: 20 } 163 | ] 164 | }, 165 | { 166 | width: 1000, 167 | crop: 'fit', 168 | text: { 169 | color: 'white', 170 | fontFamily: 'Merriweather', 171 | fontSize: 58, 172 | fontWeight: 'bold', 173 | lineSpacing: 10, 174 | lineSpacing: 10, 175 | text: headline 176 | }, 177 | position: { 178 | x: 100, 179 | y: 100, 180 | gravity: 'north_west' 181 | }, 182 | }, 183 | { 184 | publicId, 185 | width: 1000, 186 | height: 2, 187 | effects: [{ 188 | colorize: '100,co_white', 189 | opacity: 70 190 | }], 191 | position: { 192 | x: 100, 193 | y: 175, 194 | gravity: 'south_west' 195 | }, 196 | }, 197 | { 198 | width: 60, 199 | crop: 'fit', 200 | publicId: logoPublicId, 201 | position: { 202 | x: 100, 203 | y: 102, 204 | gravity: 'south_west' 205 | }, 206 | }, 207 | { 208 | text: { 209 | color: 'white', 210 | fontFamily: 'Lato', 211 | fontSize: 37, 212 | fontWeight: 'bold', 213 | text: tagline 214 | }, 215 | position: { 216 | x: 180, 217 | y: 100, 218 | gravity: 'south_west' 219 | }, 220 | }, 221 | ] 222 | }) 223 | ``` 224 | 225 | 226 | 227 | 228 | ## Learn More 229 | * [Social Media Card Templates](/templates/social-media-cards) 230 | * [getCldOgImageUrl Configuration](/getcldogimageurl/configuration) -------------------------------------------------------------------------------- /docs/src/content/docs/guides/text-overlays.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Text Overlays 3 | description: Adding text to images in Astro with Cloudinary. 4 | head: 5 | - tag: title 6 | content: Text Overlays - Astro Cloudinary 7 | --- 8 | import { Tabs, TabItem } from '@astrojs/starlight/components'; 9 | 10 | import { CldImage } from '../../../../../astro-cloudinary'; 11 | 12 | import CodeBlock from '../../../components/CodeBlock.astro'; 13 | import HeaderImage from '../../../components/HeaderImage.astro'; 14 | 15 | You can easily add text on top of your image with text-based overlays using 16 | the CldImage component. 17 | 18 | ## Example 19 | 20 | 21 | 60 | 61 | 62 | 63 | 64 | 65 | ```jsx 66 | import { CldImage } from 'astro-cloudinary'; 67 | 68 | 107 | ``` 108 | 109 | 110 | 111 | 112 | ```jsx 113 | import { getCldImageUrl } from 'astro-cloudinary/helpers'; 114 | 115 | getCldImageUrl({ 116 | width: 1335, 117 | height: 891, 118 | src: 'images/sneakers', 119 | overlays: [ 120 | { 121 | position: { 122 | x: 150, 123 | y: 125, 124 | angle: -20, 125 | gravity: 'south_east', 126 | }, 127 | text: { 128 | color: 'magenta', 129 | fontFamily: 'Source Sans Pro', 130 | fontSize: 150, 131 | fontWeight: 'black', 132 | text: 'WITH STYLE' 133 | } 134 | }, 135 | { 136 | position: { 137 | x: 140, 138 | y: 140, 139 | angle: -20, 140 | gravity: 'south_east', 141 | }, 142 | text: { 143 | color: 'white', 144 | fontFamily: 'Source Sans Pro', 145 | fontSize: 150, 146 | fontWeight: 'black', 147 | text: 'WITH STYLE' 148 | } 149 | }, 150 | ] 151 | }) 152 | ``` 153 | 154 | 155 | 156 | 157 | ## Learn More 158 | * [CldImage](/cldimage/basic-usage) 159 | * [getCldImageUrl](/getcldimageurl/basic-usage) -------------------------------------------------------------------------------- /docs/src/content/docs/guides/uploading-images-and-videos.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Uploading Images & Videos 3 | description: Learn how to upload images and videos in Astro with Cloudinary 4 | head: 5 | - tag: title 6 | content: Uploading Images & Videos - Astro Cloudinary 7 | --- 8 | import { CldUploadWidget } from '../../../../../astro-cloudinary'; 9 | 10 | import CodeBlock from '../../../components/CodeBlock.astro'; 11 | import HeaderImage from '../../../components/HeaderImage.astro'; 12 | import Button from '../../../components/Button.astro'; 13 | import DemoUploadWidgetSigned from '../../../components/DemoUploadWidgetSigned.astro'; 14 | 15 | 16 | Astro Cloudinary provides a seamless way to integrate Cloudinary's powerful upload capabilities into your Astro project. This guide will show you how to use the `CldUploadWidget` component to add upload functionality to your application. 17 | 18 | ## CldUploadWidget Component 19 | 20 | The `CldUploadWidget` component is a wrapper around Cloudinary's Upload Widget, specifically optimized for Astro projects. It allows you to easily add upload functionality with minimal configuration. 21 | 22 | ### Basic Usage 23 | 24 | Here's a simple example of how to use the `CldUploadWidget`: 25 | 26 | 27 | ```astro copy 28 | import { CldUploadWidget } from 'astro-cloudinary'; 29 | 30 | 31 | 32 | 33 | ``` 34 | 35 | 36 | Replace `` with your actual Cloudinary upload preset. 37 | 38 | ### Live Example 39 | Here's a live example of the CldUploadWidget in action: 40 | 41 | 42 | 43 | 44 | 45 | :::note 46 | All files uploaded in this demo will be deleted. 47 | ::: 48 | 49 | ### Signed Uploads 50 | For enhanced security, you can use signed uploads. This requires setting up a server-side endpoint to sign the upload parameters. 51 | 52 | 53 | ```astro copy 54 | import { CldUploadWidget } from 'astro-cloudinary'; 55 | 56 | 60 | 61 | 62 | ``` 63 | 64 | 65 | You'll need to create an API route to handle the signature. Here's an example of how your API route might look: 66 | 67 | 68 | ```typescript copy 69 | import type { APIRoute } from "astro"; 70 | import { v2 as cloudinary } from "cloudinary"; 71 | 72 | export const post: APIRoute = async ({ request }) => { 73 | const body = await request.json(); 74 | const { paramsToSign } = body; 75 | const signature = cloudinary.utils.api_sign_request( 76 | paramsToSign, 77 | process.env.CLOUDINARY_API_SECRET 78 | ); 79 | return new Response(JSON.stringify({ signature }), { 80 | status: 200, 81 | headers: { 82 | "Content-Type": "application/json", 83 | }, 84 | }); 85 | }; 86 | ``` 87 | 88 | 89 | ## Configuration Options 90 | 91 | The `CldUploadWidget` component accepts several props for customization: 92 | 93 | - `uploadPreset`: (Required) Your Cloudinary upload preset 94 | - `signatureEndpoint`: (Optional) Endpoint for signed uploads 95 | - `onUpload`: Callback function triggered after a successful upload 96 | - `onError`: Callback function triggered if an error occurs during upload 97 | 98 | For a full list of options, refer to the [CldUploadWidget Configuration](/clduploadwidget/configuration) page. 99 | 100 | ## Best Practices and Tips 101 | 102 | 1. Always use upload presets to control what can be uploaded to your Cloudinary account. 103 | 2. Implement signed uploads for enhanced security, especially in production environments. 104 | 3. Use the `onUpload` callback to handle successful uploads, such as displaying the uploaded image or updating your application state. 105 | 4. Customize the upload widget to match your application's design using CSS. 106 | 5. Consider implementing upload restrictions (file types, sizes) using Cloudinary's upload presets. 107 | 108 | ## Learn More 109 | * [CldUploadWidget Basic Usage](/clduploadwidget/basic-usage) 110 | * [CldUploadWidget Configuration](/clduploadwidget/configuration) 111 | * [Cloudinary Upload Widget Documentation](https://cloudinary.com/documentation/upload_widget) 112 | * [Cloudinary Upload API Documentation](https://cloudinary.com/documentation/image_upload_api_reference) 113 | -------------------------------------------------------------------------------- /docs/src/content/docs/installation.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Installing Astro Cloudinary 3 | description: Getting started with Astro Cloudinary. 4 | ogImageTitle: Installation 5 | head: 6 | - tag: title 7 | content: Installation - Astro Cloudinary 8 | --- 9 | import { Tabs, TabItem, Steps } from '@astrojs/starlight/components'; 10 | 11 | import { CldImage } from '../../../../astro-cloudinary'; 12 | 13 | import CodeBlock from '../../components/CodeBlock.astro'; 14 | import HeaderImage from '../../components/HeaderImage.astro'; 15 | 16 | 17 | ## Getting Started 18 | 19 | 20 |
    21 |
  1. 22 | ### Installation 23 | 24 | 25 | 26 | 27 | ``` 28 | npm install astro-cloudinary 29 | ``` 30 | 31 | 32 | 33 | 34 | ``` 35 | pnpm install astro-cloudinary 36 | ``` 37 | 38 | 39 | 40 | 41 | ``` 42 | yarn add astro-cloudinary 43 | ``` 44 | 45 | 46 | 47 |
  2. 48 |
  3. 49 | ### Configuration 50 | 51 | Add the following variable to your `.env` file. 52 | 53 | 54 | ``` 55 | PUBLIC_CLOUDINARY_CLOUD_NAME="" 56 | ``` 57 | 58 | 59 | If creating a Collection with cldAssetsLoader or managing Signed Uploads 60 | with the CldUploadWidget, additionally add: 61 | 62 | 63 | ``` 64 | PUBLIC_CLOUDINARY_API_KEY="" 65 | CLOUDINARY_API_SECRET="" 66 | ``` 67 | 68 |
  4. 69 |
70 |
71 | 72 | :::tip 73 | Don't have a Cloudinary account? Sign up for free on cloudinary.com! 74 | ::: 75 | 76 | ## Global Configuration 77 | 78 | Additional global configurations are availble to more easily manage 79 | Cloudinary environments: 80 | 81 | 82 | ``` 83 | PUBLIC_CLOUDINARY_SECURE_DISTRIBUTION="" 84 | PUBLIC_CLOUDINARY_PRIVATE_CDN="" 85 | ``` 86 | 87 | 88 | :::tip 89 | These are not all required to use the library. 90 | ::: 91 | 92 | ## Using Astro Cloudinary 93 | 94 | Learn how to add one of the Astro Cloudinary components: 95 | 96 | * [CldImage](/cldimage/basic-usage): Optimize Cloudinary images with ready-to-use transformations 97 | * [CldUploadWidget](/clduploadwidget/basic-usage): Cloudinary Upload Widget with a customizable UI 98 | * [CldVideoPlayer](/cldvideoplayer/basic-usage): Deliver Cloudinary video assets with a customizable player 99 | 100 | Or use a loader to source images and videos into Astro: 101 | 102 | * [cldAssetsLoader](/cldassetsloader/basic-usage): Load Cloudinary images and videos into Astro's Content Layer 103 | 104 | {/* * [CldOgImage](/cldogimage/basic-usage): Easy-to-use OG Image and Social Media Cards */} 105 | {/* * [CldUploadButton](/clduploadbutton/basic-usage): Drop-in button that opens the Cloudinary Upload Widget */} 106 | 107 | Or use helper methods to generate URLs: 108 | 109 | * [getCldImageUrl](/getcldimageurl/basic-usage): Construct a Cloudinary image URL using the same API as CldImage 110 | * [getCldOgImageUrl](/getcldogimageurl/basic-usage): Create a Cloudinary image URL specifically for Social Media Cards 111 | * [getCldVideoUrl](/getcldvideourl/basic-usage): Create a Cloudinary video URL using a similar API as CldImage -------------------------------------------------------------------------------- /docs/src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /docs/src/lib/util.ts: -------------------------------------------------------------------------------- 1 | import { clsx, type ClassValue } from 'clsx'; 2 | import { twMerge } from 'tailwind-merge'; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } -------------------------------------------------------------------------------- /docs/src/pages/api/sign-cloudinary-params.ts: -------------------------------------------------------------------------------- 1 | import type { APIRoute } from "astro"; 2 | import { v2 as cloudinary } from "cloudinary"; 3 | 4 | cloudinary.config({ 5 | cloud_name: import.meta.env.PUBLIC_CLOUDINARY_CLOUD_NAME, 6 | api_key: import.meta.env.PUBLIC_CLOUDINARY_API_KEY, 7 | api_secret: import.meta.env.CLOUDINARY_API_SECRET, 8 | }); 9 | 10 | export const prerender = false; 11 | 12 | export const POST: APIRoute = async ({ request }) => { 13 | const body = await request.json(); 14 | const { paramsToSign } = body; 15 | const signature = cloudinary.utils.api_sign_request(paramsToSign, import.meta.env.CLOUDINARY_API_SECRET); 16 | return Response.json({ signature }); 17 | } -------------------------------------------------------------------------------- /docs/src/styles/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | --sl-text-h1: 2.2rem; 7 | --sl-text-h2: 1.6rem; 8 | --sl-text-h3: 1.3rem; 9 | 10 | --sl-sidebar-width: 16rem; 11 | } 12 | 13 | main { 14 | 15 | @media (min-width: 1152px) { 16 | padding-right: 2rem; 17 | } 18 | 19 | } 20 | 21 | .header { 22 | 23 | @media (min-width: 50rem) { 24 | grid-template-columns: calc(var(--sl-sidebar-width) - var(--sl-nav-pad-x) - var(--sl-nav-pad-x)) 1fr auto 25 | } 26 | 27 | } 28 | 29 | .site-title { 30 | 31 | color: black; 32 | 33 | [data-theme="dark"] & { 34 | color: white 35 | } 36 | 37 | @media (prefers-color-scheme: dark) { 38 | color: white; 39 | } 40 | 41 | } 42 | 43 | .content-panel h1 { 44 | 45 | margin-top: .5rem; 46 | margin-bottom: -1rem; 47 | 48 | @media (min-width: 1152px) { 49 | margin-top: 1.5rem; 50 | margin-bottom: -.5rem; 51 | } 52 | } 53 | 54 | .content-panel +.content-panel { 55 | border: none; 56 | } 57 | 58 | .content-panel .sl-container { 59 | margin: 0 auto; 60 | } 61 | 62 | .right-sidebar { 63 | 64 | border: none; 65 | 66 | @media (min-width: 1152px) { 67 | padding-top: 6rem; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /docs/tailwind.config.mjs: -------------------------------------------------------------------------------- 1 | import starlightPlugin from '@astrojs/starlight-tailwind'; 2 | import colors from 'tailwindcss/colors'; 3 | 4 | 5 | /** @type {import('tailwindcss').Config} */ 6 | export default { 7 | content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'], 8 | theme: { 9 | extend: { 10 | colors: { 11 | gray: colors.slate, 12 | } 13 | } 14 | }, 15 | plugins: [starlightPlugin()], 16 | } 17 | -------------------------------------------------------------------------------- /docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/strict", 3 | "exclude": [ 4 | "dist" 5 | ] 6 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "astro-cloudinary", 3 | "repository": "git@github.com:cloudinary-community/astro-cloudinary.git", 4 | "author": "Colby Fayock ", 5 | "license": "MIT", 6 | "private": true, 7 | "scripts": { 8 | "release": "semantic-release", 9 | "dev": "pnpm -r dev" 10 | }, 11 | "devDependencies": { 12 | "@colbyfayock/semantic-release-pnpm": "^1.2.2", 13 | "@semantic-release/changelog": "^6.0.3", 14 | "@semantic-release/git": "^10.0.1", 15 | "semantic-release": "^23.0.0", 16 | "typescript": "^5.5.4" 17 | }, 18 | "packageManager": "pnpm@9.5.0" 19 | } 20 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "astro-cloudinary" 3 | - "docs" 4 | - "scripts" 5 | -------------------------------------------------------------------------------- /scripts/.env.example: -------------------------------------------------------------------------------- 1 | CLOUDINARY_CLOUD_NAME="" 2 | CLOUDINARY_API_KEY="" 3 | CLOUDINARY_API_SECRET="" 4 | # CLOUDINARY_ASSETS_DIRECTORY="assets" -------------------------------------------------------------------------------- /scripts/.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | node_modules -------------------------------------------------------------------------------- /scripts/assets.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "publicId": "space-jelly-cosmo-helmet", 4 | "url": "https://res.cloudinary.com/astro-cloudinary/image/upload/assets/space-jelly-cosmo-helmet.svg", 5 | "resourceType": "image" 6 | }, 7 | { 8 | "publicId": "cloudinary-blue", 9 | "url": "https://res.cloudinary.com/astro-cloudinary/image/upload/assets/cloudinary-blue.svg", 10 | "resourceType": "image" 11 | }, 12 | { 13 | "publicId": "cloudinary-white", 14 | "url": "https://res.cloudinary.com/astro-cloudinary/image/upload/assets/cloudinary-white.svg", 15 | "resourceType": "image" 16 | }, 17 | { 18 | "publicId": "astro-logo-light-gradient", 19 | "url": "https://res.cloudinary.com/astro-cloudinary/image/upload/assets/astro-logo-light-gradient.svg", 20 | "resourceType": "image" 21 | }, 22 | { 23 | "publicId": "astro-logo-dark", 24 | "url": "https://res.cloudinary.com/astro-cloudinary/image/upload/assets/astro-logo-dark.svg", 25 | "resourceType": "image" 26 | }, 27 | { 28 | "publicId": "astro-cloudinary-social-card-1.1", 29 | "url": "https://res.cloudinary.com/astro-cloudinary/image/upload/assets/astro-cloudinary-social-card-1.1.png", 30 | "resourceType": "image" 31 | }, 32 | { 33 | "publicId": "astro-cloudinary-social-card-template-1.1", 34 | "url": "https://res.cloudinary.com/astro-cloudinary/image/upload/assets/astro-cloudinary-social-card-template-1.1.png", 35 | "resourceType": "image" 36 | }, 37 | { 38 | "publicId": "astro-cloudinary-social-cards_ntewed", 39 | "url": "https://res.cloudinary.com/astro-cloudinary/image/upload/assets/astro-cloudinary-social-cards_ntewed.png", 40 | "resourceType": "image" 41 | }, 42 | { 43 | "publicId": "astro-cloudinary-upload-widget_ngil8j", 44 | "url": "https://res.cloudinary.com/astro-cloudinary/image/upload/assets/astro-cloudinary-upload-widget_ngil8j.png", 45 | "resourceType": "image" 46 | }, 47 | { 48 | "publicId": "pill-good-green_aaoi4h", 49 | "url": "https://res.cloudinary.com/astro-cloudinary/image/upload/assets/pill-good-green_aaoi4h.png", 50 | "resourceType": "image" 51 | }, 52 | { 53 | "publicId": "pill-bad-red_ceyitm", 54 | "url": "https://res.cloudinary.com/astro-cloudinary/image/upload/assets/pill-bad-red_ceyitm.png", 55 | "resourceType": "image" 56 | }, 57 | { 58 | "publicId": "white", 59 | "url": "https://res.cloudinary.com/astro-cloudinary/image/upload/assets/white.png", 60 | "resourceType": "image" 61 | }, 62 | { 63 | "publicId": "Inter-Bold.ttf", 64 | "url": "https://res.cloudinary.com/astro-cloudinary/raw/upload/v1727277943/assets/Inter-Bold-Sample.ttf", 65 | "resourceType": "raw", 66 | "type": "authenticated" 67 | } 68 | ] -------------------------------------------------------------------------------- /scripts/collections.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "publicId": "space-jelly-cosmo-helmet", 4 | "url": "https://res.cloudinary.com/astro-cloudinary/image/upload/assets/space-jelly-cosmo-helmet.svg", 5 | "resourceType": "image" 6 | }, 7 | { 8 | "publicId": "cloudinary-blue", 9 | "url": "https://res.cloudinary.com/astro-cloudinary/image/upload/assets/cloudinary-blue.svg", 10 | "resourceType": "image" 11 | }, 12 | { 13 | "publicId": "cloudinary-white", 14 | "url": "https://res.cloudinary.com/astro-cloudinary/image/upload/assets/cloudinary-white.svg", 15 | "resourceType": "image" 16 | }, 17 | { 18 | "publicId": "astro-logo-light-gradient", 19 | "url": "https://res.cloudinary.com/astro-cloudinary/image/upload/assets/astro-logo-light-gradient.svg", 20 | "resourceType": "image" 21 | }, 22 | { 23 | "publicId": "astro-logo-dark", 24 | "url": "https://res.cloudinary.com/astro-cloudinary/image/upload/assets/astro-logo-dark.svg", 25 | "resourceType": "image" 26 | }, 27 | { 28 | "publicId": "astro-cloudinary-social-card-1.1", 29 | "url": "https://res.cloudinary.com/astro-cloudinary/image/upload/assets/astro-cloudinary-social-card-1.1.png", 30 | "resourceType": "image" 31 | }, 32 | { 33 | "publicId": "astro-cloudinary-social-card-template-1.1", 34 | "url": "https://res.cloudinary.com/astro-cloudinary/image/upload/assets/astro-cloudinary-social-card-template-1.1.png", 35 | "resourceType": "image" 36 | }, 37 | { 38 | "publicId": "astro-cloudinary-social-cards_ntewed", 39 | "url": "https://res.cloudinary.com/astro-cloudinary/image/upload/assets/astro-cloudinary-social-cards_ntewed.png", 40 | "resourceType": "image" 41 | }, 42 | { 43 | "publicId": "astro-cloudinary-upload-widget_ngil8j", 44 | "url": "https://res.cloudinary.com/astro-cloudinary/image/upload/assets/astro-cloudinary-upload-widget_ngil8j.png", 45 | "resourceType": "image" 46 | }, 47 | { 48 | "publicId": "pill-good-green_aaoi4h", 49 | "url": "https://res.cloudinary.com/astro-cloudinary/image/upload/assets/pill-good-green_aaoi4h.png", 50 | "resourceType": "image" 51 | }, 52 | { 53 | "publicId": "pill-bad-red_ceyitm", 54 | "url": "https://res.cloudinary.com/astro-cloudinary/image/upload/assets/pill-bad-red_ceyitm.png", 55 | "resourceType": "image" 56 | }, 57 | { 58 | "publicId": "white", 59 | "url": "https://res.cloudinary.com/astro-cloudinary/image/upload/assets/white.png", 60 | "resourceType": "image" 61 | }, 62 | { 63 | "publicId": "Inter-Bold.ttf", 64 | "url": "https://res.cloudinary.com/astro-cloudinary/raw/upload/v1727277943/assets/Inter-Bold-Sample.ttf", 65 | "resourceType": "raw", 66 | "type": "authenticated" 67 | } 68 | ] -------------------------------------------------------------------------------- /scripts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scripts", 3 | "version": "0.0.1", 4 | "scripts": { 5 | "upload": "node upload-assets.js" 6 | }, 7 | "dependencies": { 8 | }, 9 | "devDependencies": { 10 | "cloudinary": "^1.32.0", 11 | "dotenv": "^16.0.3" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /scripts/upload-assets.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const cloudinary = require('cloudinary').v2; 3 | 4 | const assets = require('./assets.json'); 5 | 6 | cloudinary.config({ 7 | cloud_name: process.env.CLOUDINARY_CLOUD_NAME, 8 | api_key: process.env.CLOUDINARY_API_KEY, 9 | api_secret: process.env.CLOUDINARY_API_SECRET 10 | }); 11 | 12 | (async function run() { 13 | await uploadAssets(assets, { 14 | folder: process.env.CLOUDINARY_ASSETS_DIRECTORY || 'assets', 15 | }); 16 | console.log('Finished.'); 17 | })(); 18 | 19 | /** 20 | * uploadAssets 21 | */ 22 | 23 | async function uploadAssets(assets, { folder }) { 24 | console.log(`Uploading ${assets.length} assets to cloud "${process.env.CLOUDINARY_CLOUD_NAME}" in folder ${folder}...`); 25 | 26 | for ( let i = 0; i < assets.length; i++ ) { 27 | const { url, publicId, resourceType, type } = assets[i]; 28 | 29 | try { 30 | const result = await cloudinary.uploader.upload(url, { 31 | folder, 32 | public_id: publicId, 33 | resource_type: resourceType || 'image', 34 | type: type || 'upload' 35 | }); 36 | console.log(`Success: ${result.secure_url}`); 37 | } catch(e) { 38 | console.log(`Error uploading ${publicId}: ${e.message}`); 39 | } 40 | } 41 | } --------------------------------------------------------------------------------