├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ └── feature-request.md ├── PULL_REQUEST_TEMPLATE │ └── pull_request_template.md ├── dependabot.yml ├── thumbnail.png └── workflows │ └── release.yml ├── .gitignore ├── .releaserc.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── KNOWN_ISSUES.md ├── LICENSE ├── README.md ├── app ├── api │ └── speak │ │ └── route.ts ├── components │ ├── App.tsx │ ├── Controls.tsx │ ├── Visualizer.tsx │ └── icons │ │ ├── BoltIcon.tsx │ │ ├── CaretIcon.tsx │ │ ├── CogIcon.tsx │ │ ├── DownloadIcon.tsx │ │ ├── ExclamationIcon.tsx │ │ ├── FacebookIcon.tsx │ │ ├── LinkedInIcon.tsx │ │ ├── MicrophoneIcon.tsx │ │ ├── SendIcon.tsx │ │ └── XIcon.tsx ├── favicon.ico ├── fonts │ ├── ABCFavorit-Bold.otf │ ├── ABCFavorit-Bold.woff │ └── ABCFavorit-Bold.woff2 ├── globals.css ├── layout.tsx └── page.tsx ├── commitlint.config.js ├── declarations.d.ts ├── deepgram.toml ├── next.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── bg.svg ├── deepgram.svg ├── dg.png ├── dg.svg ├── headphones.svg ├── old.svg └── user-icon.svg ├── sample.env.local ├── tailwind.config.ts └── tsconfig.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Something is occurring that I think is wrong 4 | title: '' 5 | labels: "\U0001F41B bug" 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## What is the current behavior? 11 | 12 | > What's happening that seems wrong? 13 | 14 | ## Steps to reproduce 15 | 16 | > To make it faster to diagnose the root problem. Tell us how can we reproduce the bug. 17 | 18 | ## Expected behavior 19 | 20 | > What would you expect to happen when following the steps above? 21 | 22 | ## Please tell us about your environment 23 | 24 | > We want to make sure the problem isn't specific to your operating system or programming language. 25 | 26 | - **Operating System/Version:** Windows 10 27 | - **Language:** [all | TypeScript | Python | PHP | etc] 28 | - **Browser:** Chrome 29 | 30 | ## Other information 31 | 32 | > Anything else we should know? (e.g. detailed explanation, stack-traces, related issues, suggestions how to fix, links for us to have context, eg. stack overflow, codepen, etc) 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Deepgram Developer Community 4 | url: https://github.com/orgs/deepgram/discussions 5 | - name: Deepgram on Twitter 6 | url: https://twitter.com/DeepgramAI 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: I think X would be a cool addition or change. 4 | title: '' 5 | labels: "✨ enhancement" 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Proposed changes 11 | 12 | > Provide a detailed description of the change or addition you are proposing 13 | 14 | ## Context 15 | 16 | > Why is this change important to you? How would you use it? How can it benefit other users? 17 | 18 | ## Possible Implementation 19 | 20 | > Not obligatory, but suggest an idea for implementing addition or change 21 | 22 | ## Other information 23 | 24 | > Anything else we should know? (e.g. detailed explanation, related issues, links for us to have context, eg. stack overflow, codepen, etc) 25 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Proposed changes 2 | 3 | Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request. If it fixes a bug or resolves a feature request, be sure to link to that issue. 4 | 5 | ## Types of changes 6 | 7 | What types of changes does your code introduce to the Vonage for Visual Studio Code extension? 8 | _Put an `x` in the boxes that apply_ 9 | 10 | - [ ] Bugfix (non-breaking change which fixes an issue) 11 | - [ ] New feature (non-breaking change which adds functionality) 12 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 13 | - [ ] Documentation update or tests (if none of the other choices apply) 14 | 15 | ## Checklist 16 | 17 | _Put an `x` in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code._ 18 | 19 | - [ ] I have read the [CONTRIBUTING](../../CONTRIBUTING.md) doc 20 | - [ ] Lint and unit tests pass locally with my changes 21 | - [ ] I have added tests that prove my fix is effective or that my feature works 22 | - [ ] I have added necessary documentation (if appropriate) 23 | - [ ] Any dependent changes have been merged and published in downstream modules 24 | 25 | ## Further comments 26 | 27 | If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc... 28 | -------------------------------------------------------------------------------- /.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 more information: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | # https://containers.dev/guide/dependabot 6 | 7 | version: 2 8 | updates: 9 | - package-ecosystem: "devcontainers" 10 | directory: "/" 11 | schedule: 12 | interval: weekly 13 | -------------------------------------------------------------------------------- /.github/thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepgram-starters/nextjs-text-to-speech/7f27e1500c4a7b96485bbe21ed513200ddc58f4c/.github/thumbnail.png -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - "next/**" 8 | - "rc/**" 9 | - "beta/**" 10 | - "alpha/**" 11 | workflow_dispatch: 12 | 13 | jobs: 14 | release: 15 | name: Release / Node ${{ matrix.node }} 16 | strategy: 17 | matrix: 18 | node: ["20"] 19 | 20 | runs-on: ubuntu-latest 21 | 22 | permissions: 23 | contents: write 24 | 25 | steps: 26 | - uses: actions/checkout@v2 27 | 28 | - name: Set up Node 29 | uses: actions/setup-node@v2 30 | with: 31 | node-version: ${{ matrix.node }} 32 | 33 | - run: | 34 | npm ci 35 | 36 | - name: Create a release 37 | run: npx semantic-release 38 | env: 39 | GITHUB_TOKEN: ${{ secrets.GH_PUSH_TOKEN }} 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | 38 | 39 | # Contentlayer 40 | .contentlayer 41 | 42 | # vscode 43 | .vscode 44 | 45 | # container 46 | .devcontainer 47 | 48 | # pycharm 49 | .idea 50 | -------------------------------------------------------------------------------- /.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "branches": [ 3 | { "name": "main" }, 4 | { "name": "next", "channel": "next", "prerelease": true }, 5 | { "name": "rc", "channel": "rc", "prerelease": true }, 6 | { "name": "beta", "channel": "beta", "prerelease": true }, 7 | { "name": "alpha", "channel": "alpha", "prerelease": true } 8 | ], 9 | "tagFormat": "${version}", 10 | "plugins": [ 11 | "@semantic-release/commit-analyzer", 12 | "@semantic-release/release-notes-generator", 13 | [ 14 | "@semantic-release/npm", 15 | { 16 | "npmPublish": false 17 | } 18 | ], 19 | [ 20 | "@semantic-release/changelog", 21 | { 22 | "changelogFile": "CHANGELOG.md", 23 | "changelogTitle": "Change Log" 24 | } 25 | ], 26 | [ 27 | "@semantic-release/git", 28 | { 29 | "assets": ["package.json", "CHANGELOG.md"] 30 | } 31 | ], 32 | "@semantic-release/github" 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Change Log 2 | 3 | # 1.0.0 (2024-05-24) 4 | 5 | 6 | ### Features 7 | 8 | * initial commit of text to speech starter app ([ba484b3](https://github.com/deepgram-starters/nextjs-text-to-speech/commit/ba484b33b61b2e184f36dd3dff464650ff207082)) 9 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | The Deepgram developer community is filled with amazing, clever and creative people, and we're excited for you to join us. Our goal is to create safe and inclusive spaces, have meaningful conversations, and explore ways to make sure that every voice is heard and understood. 4 | 5 | #### Being a Good Community Member 6 | 7 | Because we prioritize creating a safe space for our members, we believe in actively working on how we, as individuals, can ensure a positive community environment through our own actions and mindsets. 8 | 9 | Every supportive community starts with each member. We feel it’s important to be open to others, respectful, and supportive. As part of the Deepgram community, please begin by thinking carefully about and agreeing with the following statements: 10 | 11 | - I will be welcoming to everyone at the table; 12 | - I will be patient and open to learning from everyone around me; 13 | - I will treat everyone with respect, because they deserve it; 14 | - I will be mindful of the needs and boundaries of others; 15 | 16 | We strive to create a space where we learn and grow together. Here are some other key things you can do to make the community great: 17 | 18 | ### BE HUMBLE 19 | 20 | People come from all different places, and it’s best not to make assumptions about what they think or feel. Approach others with curiosity and openness. We **all** have a lot to learn from each other. 21 | 22 | ### BE HELPFUL 23 | 24 | If someone asks for help, consider jumping in. You don’t have to be an expert to talk through a problem, suggest a resource, or help find a solution. We all have something to contribute. 25 | 26 | ### ENCOURAGE OTHERS 27 | 28 | There’s no one path to have a career in technology or to this community. Let’s engage others in ways that create opportunities for learning and fun for all of us. 29 | 30 | ## Our Pledge 31 | 32 | Everyone who participates in our community must agree to abide by our Code of Conduct. By agreeing, you help create a welcoming, respectful, and friendly community based on respect and trust. We are committed to creating a harassment-free community. 33 | 34 | These rules will be strictly enforced in any and all of our official spaces, including direct messages, social media, and physical and virtual events. Everyone who participates in these spaces is required to agree to this community code. We also ask and expect community members to observe these rules anywhere the community is meeting (for example, online chats on unofficial platforms or event after-parties). 35 | 36 | ## Our Standards 37 | 38 | ### BE RESPECTFUL 39 | 40 | Exercise consideration and respect in your speech and actions. Be willing to accept and give feedback gracefully. 41 | 42 | Don’t make offensive comments related to gender, gender identity and expression, sexual orientation, disability, mental illness, neuro(a)typicality, physical appearance, body size, race, ethnicity, immigration status, religion, experience level, socioeconomic status, nationality, or other identity markers. 43 | 44 | Additionally, don’t insult or demean others. This includes making unwelcome comments about a person’s lifestyle choices and practices, including things related to diet, health, parenting, drugs, or employment. It’s not okay to insult or demean others if it’s "just a joke." 45 | 46 | ### BE WELCOMING AND OPEN 47 | 48 | Encourage others, be supportive and willing to listen, and be willing to learn from others’ perspectives and experiences. Lead with empathy and kindness. 49 | 50 | Don’t engage in gatekeeping behaviors, like questioning the intelligence or knowledge of others as a way to prove their credentials. And don’t exclude people for prejudicial reasons. 51 | 52 | ### RESPECT PRIVACY 53 | 54 | Do not publish private communications without consent. Additionally, never disclose private aspects of a person’s personal identity without consent, except as necessary to protect them from intentional abuse. 55 | 56 | ### RESPECT PERSONAL BOUNDARIES 57 | 58 | Do not introduce gratuitous or off-topic sexual images, languages, or behavior in spaces where they are not appropriate. Never make physical contact or simulated physical contact without consent or after a request to stop. Additionally, do not continue to message others about anything if they ask you to stop or leave them alone. 59 | 60 | #### BE A GOOD NEIGHBOR 61 | 62 | Contribute to the community in a positive and thoughtful way. Consider what’s best for the overall community. Do not make threats of violence, intimidate others, incite violence or intimidation against others, incite self-harm, stalk, follow, or otherwise harass others. Be mindful of your surroundings and of your fellow participants. 63 | 64 | Alert community leaders if you notice a dangerous situation, someone in distress, or violations of this Code of Conduct, even if they seem inconsequential. 65 | 66 | # Additional rules for online spaces 67 | 68 | For Deepgram’s official online spaces, like our YouTube & Twitch chats, we have some additional rules. Any of the following behaviors can result in a ban without warning. 69 | 70 | ### DON'T SPAM 71 | 72 | Don't spam. We'll ban you. 73 | 74 | ### KEEP IT LEGAL 75 | 76 | If it’s illegal, it’s not allowed on our websites or in our online spaces. Please don’t share links to pirated material or other nefarious things. 77 | 78 | ### NO TROLLING 79 | 80 | Please be earnest. Don’t use excessive sarcasm to annoy or undermine other people. And don’t bait them with bad faith comments or abuse. 81 | 82 | ### PORNOGRAPHY AND OTHER NSFW STUFF 83 | 84 | Please don’t post it or link to it. It doesn’t belong in our online spaces. 85 | 86 | ### FOUL AND GRAPHIC LANGUAGE 87 | 88 | Please do not use excessive curse words. Additionally, do not use graphic sexual or violent language — again, think of our spaces as places for people of all ages. 89 | 90 | # Enforcement & Reporting 91 | 92 | If you are being harassed by a member of the Deepgram developer community, if you observe someone else being harassed, or you experience actions or behaviors that are contrary to our Code of Conduct, please report the behavior by contacting our team at [devrel@deepgram.com](mailto:devrel@deepgram.com). 93 | 94 | ## Enforcement Guidelines 95 | 96 | Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: 97 | 98 | ### 1. Correction 99 | 100 | **_Community Impact:_** Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. 101 | 102 | **_Consequence:_** A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. 103 | 104 | ### 2. Warning 105 | 106 | **_Community Impact:_** A violation through a single incident or series of actions. 107 | 108 | **_Consequence:_** A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. 109 | 110 | ### 3. Temporary Ban 111 | 112 | **_Community Impact:_** A serious violation of community standards, including sustained inappropriate behavior. 113 | 114 | **_Consequence:_** A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. 115 | 116 | ### 4. Permanent Ban 117 | 118 | **_Community Impact:_** Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. 119 | 120 | **_Consequence:_** A permanent ban from any sort of public interaction within the community. 121 | 122 | # Attribution 123 | 124 | This Code of Conduct is adapted from: 125 | 126 | - Contributor Covenant, version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct 127 | - https://eventhandler.community/conduct/, which itself is inspired by Quest, who in turn provides credit to Scripto, the #botALLY Code of Conduct, the LGBTQ in Tech code of Conduct, and the XOXO Code of Conduct. 128 | 129 | Community Impact Guidelines, which were copied from InnerSource Commons, were inspired by Mozilla’s code of conduct enforcement ladder. 130 | 131 | For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. 132 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Want to contribute to this project? We ❤️ it! 4 | 5 | Here are a few types of contributions that we would be interested in hearing about. 6 | 7 | - Bug fixes 8 | - If you find a bug, please first report it using Github Issues. 9 | - Issues that have already been identified as a bug will be labeled `🐛 bug`. 10 | - If you'd like to submit a fix for a bug, send a Pull Request from your own fork and mention the Issue number. 11 | - Include a test that isolates the bug and verifies that it was fixed. 12 | - New Features 13 | - If you'd like to accomplish something in the extension that it doesn't already do, describe the problem in a new Github Issue. 14 | - Issues that have been identified as a feature request will be labeled `✨ enhancement`. 15 | - If you'd like to implement the new feature, please wait for feedback from the project maintainers before spending 16 | too much time writing the code. In some cases, `✨ enhancement`s may not align well with the project objectives at 17 | the time. 18 | - Tests, Documentation, Miscellaneous 19 | - If you think the test coverage could be improved, the documentation could be clearer, you've got an alternative 20 | implementation of something that may have more advantages, or any other change we would still be glad hear about 21 | it. 22 | - If its a trivial change, go ahead and send a Pull Request with the changes you have in mind 23 | - If not, open a Github Issue to discuss the idea first. 24 | - Snippets 25 | - To add snippets: 26 | - Add a directory in the `snippets` folder with the name of the language. 27 | - Add one or more files in the language directory with snippets. 28 | - Update the `package.json` to include the snippets you added. 29 | 30 | We also welcome anyone to work on any existing issues with the `👋🏽 good first issue` tag. 31 | 32 | ## Requirements 33 | 34 | For a contribution to be accepted: 35 | 36 | - The test suite must be complete and pass 37 | - Code must follow existing styling conventions 38 | - Commit messages must be descriptive. Related issues should be mentioned by number. 39 | 40 | If the contribution doesn't meet these criteria, a maintainer will discuss it with you on the Issue. You can still 41 | continue to add more commits to the branch you have sent the Pull Request from. 42 | 43 | ## How To 44 | 45 | 1. Fork this repository on GitHub. 46 | 1. Clone/fetch your fork to your local development machine. 47 | 1. Create a new branch (e.g. `issue-12`, `feat.add_foo`, etc) and check it out. 48 | 1. Make your changes and commit them. (Did the tests pass? No linting errors?) 49 | 1. Push your new branch to your fork. (e.g. `git push myname issue-12`) 50 | 1. Open a Pull Request from your new branch to the original fork's `main` branch. 51 | -------------------------------------------------------------------------------- /KNOWN_ISSUES.md: -------------------------------------------------------------------------------- 1 | # Known Issues 2 | 3 | This is a list of known issues. For the latest list of all issues see the [Github Issues page](https://github.com/deepgram-starters/nextjs-text-to-speech/issues). 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Deepgram 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Next.js Text-to-Speech Starter 2 | 3 | [![Discord](https://dcbadge.vercel.app/api/server/xWRaCDBtW4?style=flat)](https://discord.gg/xWRaCDBtW4) 4 | 5 | The purpose of this demo is to showcase how you can build a NextJS speech to text app using [Deepgram](https://deepgram.com/). 6 | 7 | ## Live Demo 8 | You can see the demo in action on Vercel: https://nextjs-text-to-speech-seven.vercel.app/ 9 | 10 | ## Demo features 11 | 12 | - Capture streaming audio using [Deepgram Streaming Speech to Text](https://developers.deepgram.com/docs/getting-started-with-live-streaming-audio). 13 | 14 | ## What is Deepgram? 15 | 16 | [Deepgram’s](https://deepgram.com/) voice AI platform provides APIs for speech-to-text, text-to-speech, and full speech-to-speech voice agents. Over 200,000+ developers use Deepgram to build voice AI products and features. 17 | 18 | ## Sign-up to Deepgram 19 | 20 | Want to start building using this project? [Sign-up now for Deepgram and create an API key](https://console.deepgram.com/signup?jump=keys). 21 | 22 | ## Quickstart 23 | 24 | ### Manual 25 | 26 | Follow these steps to get started with this starter application. 27 | 28 | #### Clone the repository 29 | 30 | Go to GitHub and [clone the repository](https://github.com/deepgram-starters/nextjs-text-to-speech). 31 | 32 | #### Install dependencies 33 | 34 | Install the project dependencies. 35 | 36 | ```bash 37 | npm install 38 | ``` 39 | 40 | #### Edit the config file 41 | 42 | Copy the code from `sample.env.local` and create a new file called `.env.local`. 43 | 44 | ```bash 45 | DEEPGRAM_API_KEY=YOUR-DG-API-KEY 46 | ``` 47 | 48 | For `DEEPGRAM_API_KEY` paste in the key you generated in the [Deepgram console](https://console.deepgram.com/). 49 | 50 | #### Run the application 51 | 52 | Once running, you can [access the application in your browser](http://localhost:3000). 53 | 54 | ```bash 55 | npm run dev 56 | ``` 57 | 58 | ## Issue Reporting 59 | 60 | If you have found a bug or if you have a feature request, please report them at this repository issues section. Please do not report security vulnerabilities on the public GitHub issue tracker. The [Security Policy](./SECURITY.md) details the procedure for contacting Deepgram. 61 | 62 | ## Getting Help 63 | 64 | We love to hear from you so if you have questions, comments or find a bug in the project, let us know! You can either: 65 | 66 | - [Open an issue in this repository](https://github.com/deepgram-starters/nextjs-text-to-speech/issues) 67 | - [Join the Deepgram Github Discussions Community](https://github.com/orgs/deepgram/discussions) 68 | - [Join the Deepgram Discord Community](https://discord.gg/xWRaCDBtW4) 69 | 70 | ## Author 71 | 72 | [Deepgram](https://deepgram.com) 73 | 74 | ## License 75 | 76 | This project is licensed under the MIT license. See the [LICENSE](./LICENSE) file for more info. 77 | -------------------------------------------------------------------------------- /app/api/speak/route.ts: -------------------------------------------------------------------------------- 1 | import { createClient } from "@deepgram/sdk"; 2 | import { NextRequest, NextResponse } from "next/server"; 3 | 4 | export const revalidate = 0; 5 | 6 | /** 7 | * Return a stream from the API 8 | * @param {NextRequest} req - The HTTP request 9 | * @returns {Promise} A NextResponse with the streamable response 10 | */ 11 | export async function POST(request: NextRequest) { 12 | // gotta use the request object to invalidate the cache every request :vomit: 13 | const url = request.url; 14 | const deepgram = createClient(process.env.DEEPGRAM_API_KEY ?? ""); 15 | 16 | const model = request.nextUrl.searchParams.get("model") ?? "aura-2-thalia-en"; 17 | const message = await request.json(); 18 | 19 | console.log(model, message); 20 | 21 | const result = await deepgram.speak.request(message, { model }); 22 | const stream = await result.getStream(); 23 | const headers = await result.getHeaders(); 24 | 25 | const response = new NextResponse(stream, { headers }); 26 | response.headers.set("Surrogate-Control", "no-store"); 27 | response.headers.set( 28 | "Cache-Control", 29 | "s-maxage=0, no-store, no-cache, must-revalidate, proxy-revalidate", 30 | ); 31 | response.headers.set("Expires", "0"); 32 | 33 | return response; 34 | } 35 | -------------------------------------------------------------------------------- /app/components/App.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useNowPlaying } from "react-nowplaying"; 4 | import dynamic from "next/dynamic"; 5 | import { useState } from "react"; 6 | import Visualizer from "./Visualizer"; 7 | 8 | const Controls = dynamic(() => import("./Controls"), { 9 | ssr: false, 10 | }); 11 | 12 | const App: () => JSX.Element = () => { 13 | const [context, setContext] = useState(); 14 | const { player } = useNowPlaying(); 15 | 16 | return ( 17 | <> 18 |
19 |
20 |
21 | {/* height 100% minus 8rem */} 22 |
23 | {context && player && ( 24 | 25 | )} 26 |
27 | { 29 | setContext(ctx); 30 | }} 31 | /> 32 |
33 |
34 |
35 |
36 |
37 | 38 | ); 39 | }; 40 | 41 | export default App; 42 | -------------------------------------------------------------------------------- /app/components/Controls.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | BaseSyntheticEvent, 3 | ChangeEvent, 4 | Dispatch, 5 | KeyboardEvent, 6 | MouseEvent, 7 | SetStateAction, 8 | useCallback, 9 | useState, 10 | } from "react"; 11 | 12 | import { SendIcon } from "./icons/SendIcon"; 13 | import { useNowPlaying } from "react-nowplaying"; 14 | import TextareaAutosize from "react-textarea-autosize"; 15 | import { isBrowser, isDesktop, isMacOs } from "react-device-detect"; 16 | 17 | const Controls = ({ 18 | callback, 19 | }: { 20 | callback: Dispatch>; 21 | }) => { 22 | const [text, setText] = useState(); 23 | const { stop: stopAudio, play: playAudio } = useNowPlaying(); 24 | 25 | const sendText = useCallback( 26 | async (event: BaseSyntheticEvent) => { 27 | callback(new (window.AudioContext || window.webkitAudioContext)()); 28 | 29 | stopAudio(); 30 | 31 | const model = "aura-2-thalia-en"; 32 | 33 | const response = await fetch(`/api/speak?model=${model}`, { 34 | cache: "no-store", 35 | method: "POST", 36 | body: JSON.stringify({ text }), 37 | }); 38 | 39 | stopAudio(); 40 | setText(""); 41 | 42 | playAudio(await response.blob(), "audio/mp3"); 43 | }, 44 | // eslint-disable-next-line react-hooks/exhaustive-deps 45 | [text], 46 | ); 47 | 48 | return ( 49 |
50 |
51 |
52 |
53 | ) => { 55 | if (event.key !== "Enter" || !isDesktop) return; 56 | 57 | /** 58 | * On a desktop/browser they press Ctrl+Enter or Cmd+Enter to submit the message. 59 | */ 60 | if ((isMacOs && event.metaKey) || (!isMacOs && event.ctrlKey)) { 61 | sendText(event); 62 | } 63 | }} 64 | rows={1} 65 | spellCheck={false} 66 | autoCorrect="off" 67 | className="py-2 md:py-4 -mb-[0.4rem] min-h-10 rounded-tl-[2rem] rounded-bl-[2rem] overflow-hidden sm:px-8 w-full resize-none bg-[#101014] text-light-900 border-0 text-sm sm:text-base outline-none focus:ring-0" 68 | placeholder={`Enter text to turn into speech... ${isDesktop && isBrowser && (isMacOs ? "Press ⌘ + Enter to submit." : "Press Ctrl + Enter to submit.")}`} 69 | value={text} 70 | onChange={(event: ChangeEvent) => { 71 | setText(event.target.value); 72 | }} 73 | /> 74 |
75 |
76 | 77 |
78 | 88 |
89 |
90 |
91 | ); 92 | }; 93 | 94 | export default Controls; 95 | -------------------------------------------------------------------------------- /app/components/Visualizer.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from "react"; 2 | 3 | type AudioInput = MediaStream | HTMLAudioElement; 4 | 5 | const interpolateColor = ( 6 | startColor: number[], 7 | endColor: number[], 8 | factor: number, 9 | ): number[] => { 10 | const result = []; 11 | for (let i = 0; i < startColor.length; i++) { 12 | result[i] = Math.round( 13 | startColor[i] + factor * (endColor[i] - startColor[i]), 14 | ); 15 | } 16 | return result; 17 | }; 18 | 19 | interface VisualizerProps { 20 | source: AudioInput; 21 | context?: AudioContext; 22 | } 23 | 24 | const Visualizer: React.FC = ({ source, context }) => { 25 | const canvasRef = useRef(null); 26 | 27 | if (!context) { 28 | context = new (window.AudioContext || window.webkitAudioContext)(); 29 | } 30 | 31 | const analyser = context.createAnalyser(); 32 | const dataArray = new Uint8Array(analyser.frequencyBinCount); 33 | 34 | useEffect(() => { 35 | let audioSource: AudioNode; 36 | 37 | if (source instanceof MediaStream) { 38 | audioSource = context!.createMediaStreamSource(source); 39 | } else { 40 | audioSource = context!.createMediaElementSource(source); 41 | audioSource.connect(context!.destination); 42 | } 43 | 44 | audioSource.connect(analyser); 45 | draw(); 46 | 47 | return () => { 48 | audioSource.disconnect(); 49 | }; 50 | // eslint-disable-next-line react-hooks/exhaustive-deps 51 | }, [source]); 52 | 53 | const draw = (): void => { 54 | const canvas = canvasRef.current; 55 | if (!canvas) return; 56 | 57 | canvas.style.width = "100%"; 58 | canvas.style.height = "100%"; 59 | canvas.width = canvas.offsetWidth; 60 | canvas.height = canvas.offsetHeight; 61 | 62 | const canvasContext = canvas.getContext("2d"); 63 | const width = canvas.width; 64 | const height = canvas.height; 65 | 66 | requestAnimationFrame(draw); 67 | analyser.getByteFrequencyData(dataArray); 68 | 69 | if (!canvasContext) return; 70 | 71 | canvasContext.clearRect(0, 0, width, height); 72 | 73 | const barWidth = 10; 74 | let x = 0; 75 | const startColor = [19, 239, 147]; 76 | const endColor = [20, 154, 251]; 77 | 78 | for (const value of dataArray) { 79 | const barHeight = (value / 255) * height * 2; 80 | const interpolationFactor = value / 255; 81 | const color = interpolateColor(startColor, endColor, interpolationFactor); 82 | 83 | canvasContext.fillStyle = `rgba(${color[0]}, ${color[1]}, ${color[2]}, 0.1)`; 84 | canvasContext.fillRect(x, height - barHeight, barWidth, barHeight); 85 | x += barWidth; 86 | } 87 | }; 88 | 89 | return ; 90 | }; 91 | 92 | export default Visualizer; 93 | -------------------------------------------------------------------------------- /app/components/icons/BoltIcon.tsx: -------------------------------------------------------------------------------- 1 | export const BoltIcon = ({ className = "" }) => { 2 | return ( 3 | 9 | {" "} 10 | 11 | ); 12 | }; 13 | -------------------------------------------------------------------------------- /app/components/icons/CaretIcon.tsx: -------------------------------------------------------------------------------- 1 | export const CaretIcon = ({ className = "" }) => { 2 | return ( 3 | 9 | 10 | 11 | ); 12 | }; 13 | -------------------------------------------------------------------------------- /app/components/icons/CogIcon.tsx: -------------------------------------------------------------------------------- 1 | export const CogIcon = ({ className = "" }) => ( 2 | 9 | 14 | 19 | 20 | ); 21 | -------------------------------------------------------------------------------- /app/components/icons/DownloadIcon.tsx: -------------------------------------------------------------------------------- 1 | export const DownloadIcon = ({ className = "" }) => ( 2 | 9 | 14 | 15 | ); 16 | -------------------------------------------------------------------------------- /app/components/icons/ExclamationIcon.tsx: -------------------------------------------------------------------------------- 1 | export const ExclamationIcon = () => { 2 | return <>⚠️; 3 | }; 4 | -------------------------------------------------------------------------------- /app/components/icons/FacebookIcon.tsx: -------------------------------------------------------------------------------- 1 | export const FacebookIcon = ({ className = "" }) => { 2 | return ( 3 | 9 | 10 | 11 | ); 12 | }; 13 | -------------------------------------------------------------------------------- /app/components/icons/LinkedInIcon.tsx: -------------------------------------------------------------------------------- 1 | export const LinkedInIcon = ({ className = "" }) => { 2 | return ( 3 | 9 | 10 | 11 | ); 12 | }; 13 | -------------------------------------------------------------------------------- /app/components/icons/MicrophoneIcon.tsx: -------------------------------------------------------------------------------- 1 | export const MicrophoneIcon = ({ 2 | micOpen, 3 | className, 4 | ...rest 5 | }: { 6 | micOpen: boolean; 7 | className?: string; 8 | }) => { 9 | if (micOpen) { 10 | return ( 11 | 17 | 18 | 19 | ); 20 | } 21 | 22 | return ( 23 | 29 | 30 | 31 | ); 32 | }; 33 | -------------------------------------------------------------------------------- /app/components/icons/SendIcon.tsx: -------------------------------------------------------------------------------- 1 | export const SendIcon = ({ className, ...rest }: { className?: string }) => { 2 | return ( 3 | 9 | 10 | 11 | ); 12 | }; 13 | -------------------------------------------------------------------------------- /app/components/icons/XIcon.tsx: -------------------------------------------------------------------------------- 1 | export const XIcon = ({ className = "" }) => { 2 | return ( 3 | 9 | 10 | 11 | ); 12 | }; 13 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepgram-starters/nextjs-text-to-speech/7f27e1500c4a7b96485bbe21ed513200ddc58f4c/app/favicon.ico -------------------------------------------------------------------------------- /app/fonts/ABCFavorit-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepgram-starters/nextjs-text-to-speech/7f27e1500c4a7b96485bbe21ed513200ddc58f4c/app/fonts/ABCFavorit-Bold.otf -------------------------------------------------------------------------------- /app/fonts/ABCFavorit-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepgram-starters/nextjs-text-to-speech/7f27e1500c4a7b96485bbe21ed513200ddc58f4c/app/fonts/ABCFavorit-Bold.woff -------------------------------------------------------------------------------- /app/fonts/ABCFavorit-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepgram-starters/nextjs-text-to-speech/7f27e1500c4a7b96485bbe21ed513200ddc58f4c/app/fonts/ABCFavorit-Bold.woff2 -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | /** 6 | * General stuff 7 | */ 8 | :root { 9 | background: #0b0b0c; 10 | font-size: 16px; 11 | color-scheme: dark; 12 | } 13 | 14 | @media only screen and (min-width: 2000px) { 15 | :root { 16 | font-size: 22px; 17 | } 18 | } 19 | 20 | body { 21 | color: rgba(255, 255, 255, 0.87); 22 | background: #0b0b0c url("/bg.svg") no-repeat top center fixed; 23 | -webkit-background-size: cover; 24 | -moz-background-size: cover; 25 | -o-background-size: cover; 26 | background-size: cover; 27 | } 28 | 29 | * { 30 | /* outline: 1px solid red; */ 31 | } 32 | 33 | @layer utilities { 34 | 35 | .gradient-shadow { 36 | box-shadow: 37 | -1rem 0px 2rem 0px #13ef9335, 38 | 1rem 0px 2rem 0px #149afb35; 39 | } 40 | } 41 | 42 | /* Additional vertical padding used by kbd tag. */ 43 | .py-05 { 44 | padding-top: 0.125rem; 45 | padding-bottom: 0.125rem; 46 | } 47 | -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import { Inter } from "next/font/google"; 2 | import { NowPlayingContextProvider } from "react-nowplaying"; 3 | import classNames from "classnames"; 4 | import localFont from "next/font/local"; 5 | import type { Metadata, Viewport } from "next"; 6 | 7 | import "./globals.css"; 8 | 9 | const inter = Inter({ subsets: ["latin"] }); 10 | const favorit = localFont({ 11 | src: "./fonts/ABCFavorit-Bold.woff2", 12 | variable: "--font-favorit", 13 | }); 14 | 15 | export const viewport: Viewport = { 16 | themeColor: "#000000", 17 | initialScale: 1, 18 | width: "device-width", 19 | // maximumScale: 1, hitting accessability 20 | }; 21 | 22 | export const metadata: Metadata = { 23 | metadataBase: new URL("https://aura-tts-demo.deepgram.com"), 24 | title: "Deepgram AI Agent", 25 | description: `Deepgram's AI Agent Demo shows just how fast Speech-to-Text and Text-to-Speech can be.`, 26 | robots: { 27 | index: false, 28 | follow: false, 29 | }, 30 | }; 31 | 32 | export default function RootLayout({ 33 | children, 34 | }: { 35 | children: React.ReactNode; 36 | }) { 37 | return ( 38 | 39 | 45 | {children} 46 | 47 | 48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /app/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import Image from "next/image"; 4 | import App from "./components/App"; 5 | import { XIcon } from "./components/icons/XIcon"; 6 | import { LinkedInIcon } from "./components/icons/LinkedInIcon"; 7 | import { FacebookIcon } from "./components/icons/FacebookIcon"; 8 | import GitHubButton from "react-github-btn"; 9 | 10 | const Home = () => { 11 | return ( 12 | <> 13 |
14 | {/* height 4rem */} 15 |
16 |
17 |
18 | 19 | Deepgram Logo 27 | 28 |
29 |
30 | 31 | 38 | Star 39 | 40 | 41 | 42 | 43 | 48 | Get an API Key 49 | 50 | 51 |
52 |
53 |
54 | 55 | {/* height 100% minus 8rem */} 56 |
57 | 58 |
59 | 60 | {/* height 4rem */} 61 |
62 | 120 |
121 |
122 | 123 | ); 124 | }; 125 | 126 | export default Home; 127 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { extends: ["@commitlint/config-conventional"] }; 2 | -------------------------------------------------------------------------------- /declarations.d.ts: -------------------------------------------------------------------------------- 1 | interface Window { 2 | webkitAudioContext: typeof AudioContext; 3 | } 4 | -------------------------------------------------------------------------------- /deepgram.toml: -------------------------------------------------------------------------------- 1 | [meta] 2 | title = "Next.js Text-to-Speech Starter" 3 | description = "Get started using Deepgram's Text-to-Speech with this Next.js demo app" 4 | author = "Deepgram DX Team (https://developers.deepgram.com)" 5 | useCase = "TTS" 6 | language = "JavaScript" 7 | framework = "Next" 8 | 9 | [build] 10 | command = "npm install" 11 | 12 | [config] 13 | sample = "sample.env.local" 14 | output = ".env.local" 15 | 16 | [post-build] 17 | message = "Run `npm run dev` to get up and running locally." 18 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | 3 | const nextConfig = { 4 | reactStrictMode: false, 5 | }; 6 | 7 | module.exports = nextConfig; 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextjs-text-to-speech", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@deepgram/sdk": "^3.3.0", 13 | "classnames": "^2.5.1", 14 | "next": "^14.1.3", 15 | "react": "^18", 16 | "react-device-detect": "^2.2.3", 17 | "react-dom": "^18", 18 | "react-github-btn": "^1.4.0", 19 | "react-nowplaying": "^1.5.1", 20 | "react-syntax-highlighter": "^15.5.0", 21 | "react-textarea-autosize": "^8.5.3" 22 | }, 23 | "devDependencies": { 24 | "@commitlint/cli": "^19.1.0", 25 | "@commitlint/config-conventional": "^19.1.0", 26 | "@semantic-release/changelog": "^6.0.3", 27 | "@semantic-release/git": "^10.0.1", 28 | "@types/node": "^20", 29 | "@types/react": "^18", 30 | "@types/react-dom": "^18", 31 | "autoprefixer": "^10.0.1", 32 | "eslint": "^8", 33 | "eslint-config-next": "14.0.1", 34 | "husky": "^9.0.11", 35 | "postcss": "^8", 36 | "pretty-quick": "^4.0.0", 37 | "tailwindcss": "^3.4.1", 38 | "typescript": "^5" 39 | }, 40 | "husky": { 41 | "hooks": { 42 | "pre-commit": "pretty-quick --staged", 43 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/bg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 13 | 15 | 17 | 19 | 21 | 23 | 25 | 27 | 29 | 31 | 33 | 35 | 37 | 39 | 41 | 43 | 45 | 46 | 47 | 49 | 51 | 53 | 55 | 57 | 59 | 61 | 63 | 65 | 67 | 69 | 71 | 73 | 75 | 77 | 79 | 81 | 82 | 83 | 85 | 87 | 89 | 91 | 93 | 95 | 97 | 99 | 101 | 103 | 105 | 107 | 109 | 111 | 113 | 115 | 117 | 118 | 119 | 121 | 123 | 125 | 127 | 129 | 131 | 132 | 133 | 135 | 137 | 139 | 141 | 143 | 145 | 147 | 149 | 151 | 153 | 155 | 157 | 159 | 161 | 163 | 164 | 165 | 167 | 169 | 171 | 173 | 175 | 177 | 179 | 181 | 183 | 185 | 187 | 189 | 191 | 193 | 195 | 196 | 197 | 199 | 201 | 203 | 205 | 207 | 209 | 211 | 213 | 215 | 217 | 219 | 221 | 223 | 225 | 227 | 228 | 229 | 231 | 233 | 235 | 237 | 239 | 241 | 243 | 245 | 247 | 249 | 251 | 253 | 255 | 257 | 259 | 260 | 261 | 262 | 263 | 265 | 267 | 269 | 271 | 273 | 275 | 277 | 279 | 281 | 283 | 285 | 287 | 289 | 291 | 293 | 295 | 296 | 297 | 299 | 301 | 303 | 305 | 307 | 309 | 311 | 313 | 315 | 317 | 319 | 321 | 323 | 325 | 327 | 329 | 330 | 331 | 333 | 335 | 337 | 339 | 341 | 343 | 345 | 347 | 349 | 351 | 353 | 355 | 357 | 359 | 361 | 363 | 364 | 365 | 367 | 369 | 371 | 373 | 375 | 377 | 379 | 381 | 383 | 385 | 387 | 389 | 391 | 393 | 395 | 396 | 397 | 399 | 401 | 403 | 405 | 407 | 409 | 411 | 413 | 415 | 417 | 419 | 421 | 423 | 425 | 427 | 429 | 431 | 432 | 433 | 435 | 437 | 439 | 441 | 443 | 445 | 447 | 449 | 451 | 453 | 455 | 457 | 459 | 461 | 463 | 465 | 467 | 468 | 469 | 470 | 471 | 473 | 474 | 475 | 476 | 477 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | -------------------------------------------------------------------------------- /public/deepgram.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /public/dg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepgram-starters/nextjs-text-to-speech/7f27e1500c4a7b96485bbe21ed513200ddc58f4c/public/dg.png -------------------------------------------------------------------------------- /public/dg.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /public/headphones.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/old.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | -------------------------------------------------------------------------------- /public/user-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /sample.env.local: -------------------------------------------------------------------------------- 1 | DEEPGRAM_API_KEY= 2 | -------------------------------------------------------------------------------- /tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | const config: Config = { 4 | content: [ 5 | "./pages/**/*.{js,ts,jsx,tsx,mdx}", 6 | "./components/**/*.{js,ts,jsx,tsx,mdx}", 7 | "./app/**/*.{js,ts,jsx,tsx,mdx}", 8 | ], 9 | theme: { 10 | extend: { 11 | animation: { 12 | // Bounces 5 times 1s equals 5 seconds 13 | "ping-short": "ping 1s ease-in-out 5", 14 | }, 15 | screens: { 16 | betterhover: { raw: "(hover: hover)" }, 17 | }, 18 | transitionProperty: { 19 | height: "height", 20 | width: "width", 21 | }, 22 | dropShadow: { 23 | glowBlue: [ 24 | "0px 0px 2px #000", 25 | "0px 0px 4px #000", 26 | "0px 0px 30px #0141ff", 27 | "0px 0px 100px #0141ff80", 28 | ], 29 | glowRed: [ 30 | "0px 0px 2px #f00", 31 | "0px 0px 4px #000", 32 | "0px 0px 15px #ff000040", 33 | "0px 0px 30px #f00", 34 | "0px 0px 100px #ff000080", 35 | ], 36 | }, 37 | backgroundImage: { 38 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", 39 | "gradient-conic": 40 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", 41 | }, 42 | fontFamily: { 43 | favorit: ["var(--font-favorit)"], 44 | inter: ["Inter", "Arial", "sans serif"], 45 | }, 46 | }, 47 | }, 48 | }; 49 | export default config; 50 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2015", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | --------------------------------------------------------------------------------