├── .editorconfig ├── .github ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── FUNDING.yml ├── hooks │ ├── commit-msg │ └── pre-commit ├── labels.yml ├── problemMatchers │ ├── eslint.json │ └── tsc.json ├── renovate.json └── workflows │ ├── continuous-delivery.yml │ ├── continuous-integration.yml │ ├── generated-file-validations.yml │ ├── labelsync.yml │ ├── publish-documentation.yml │ └── publish.yml ├── .gitignore ├── .prettierignore ├── .prettierrc.mjs ├── .vscode ├── extensions.json └── settings.json ├── .yarn ├── plugins │ └── @yarnpkg │ │ └── plugin-git-hooks.cjs └── releases │ └── yarn-4.9.2.cjs ├── .yarnrc.yml ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── assets ├── dark_mode │ ├── compact_mode.png │ ├── normal_conversation.png │ └── with_embed.png ├── iconography │ ├── discord-components-apple-startup.psd │ ├── discord-components-logo.psd │ └── discord-components-opengraphpsd.psd ├── light_mode │ ├── compact_mode.png │ ├── normal_conversation.png │ └── with_embed.png └── readme-templates │ ├── CONTRIBUTING.md │ ├── CORE_NOTES.md │ ├── CORE_USAGE.md │ ├── DESCRIPTION.md │ ├── FEATURES.md │ ├── HEADER.md │ ├── REACT_NOTES.md │ ├── REACT_USAGE.md │ └── SCREENSHOTS.md ├── eslint.config.js ├── package.json ├── packages ├── core │ ├── .cliff-jumperrc.yml │ ├── CHANGELOG.md │ ├── README.md │ ├── cliff.toml │ ├── demo │ │ ├── index.css │ │ ├── index.html │ │ └── index.js │ ├── package.json │ ├── src │ │ ├── components │ │ │ ├── _private │ │ │ │ ├── DiscordMediaAttachmentStyles.ts │ │ │ │ ├── DiscordMediaLifecycle.ts │ │ │ │ ├── DiscordPlaybackControlStyles.ts │ │ │ │ └── DiscordVolumeControlStyles.ts │ │ │ ├── discord-action-row │ │ │ │ └── DiscordActionRow.ts │ │ │ ├── discord-attachments │ │ │ │ └── DiscordAttachments.ts │ │ │ ├── discord-audio-attachment │ │ │ │ └── DiscordAudioAttachment.ts │ │ │ ├── discord-author-info │ │ │ │ └── DiscordAuthorInfo.ts │ │ │ ├── discord-bold │ │ │ │ └── DiscordBold.ts │ │ │ ├── discord-button │ │ │ │ └── DiscordButton.ts │ │ │ ├── discord-code │ │ │ │ └── DiscordCode.ts │ │ │ ├── discord-command │ │ │ │ └── DiscordCommand.ts │ │ │ ├── discord-custom-emoji │ │ │ │ └── DiscordCustomEmoji.ts │ │ │ ├── discord-embed-description │ │ │ │ └── DiscordEmbedDescription.ts │ │ │ ├── discord-embed-field │ │ │ │ └── DiscordEmbedField.ts │ │ │ ├── discord-embed-fields │ │ │ │ └── DiscordEmbedFields.ts │ │ │ ├── discord-embed-footer │ │ │ │ └── DiscordEmbedFooter.ts │ │ │ ├── discord-embed │ │ │ │ └── DiscordEmbed.ts │ │ │ ├── discord-file-attachment │ │ │ │ └── DiscordFileAttachment.ts │ │ │ ├── discord-header │ │ │ │ └── DiscordHeader.ts │ │ │ ├── discord-image-attachment │ │ │ │ └── DiscordImageAttachment.ts │ │ │ ├── discord-input-text │ │ │ │ └── DiscordInputText.ts │ │ │ ├── discord-invite │ │ │ │ └── DiscordInvite.ts │ │ │ ├── discord-italic │ │ │ │ └── DiscordItalic.ts │ │ │ ├── discord-link │ │ │ │ └── DiscordLink.ts │ │ │ ├── discord-list-item │ │ │ │ └── DiscordListItem.ts │ │ │ ├── discord-mention │ │ │ │ └── DiscordMention.ts │ │ │ ├── discord-message │ │ │ │ └── DiscordMessage.ts │ │ │ ├── discord-messages │ │ │ │ └── DiscordMessages.ts │ │ │ ├── discord-modal │ │ │ │ └── DiscordModal.ts │ │ │ ├── discord-ordered-list │ │ │ │ └── DiscordOrderedList.ts │ │ │ ├── discord-poll-answer │ │ │ │ └── DiscordPollAnswer.ts │ │ │ ├── discord-poll │ │ │ │ └── DiscordPoll.ts │ │ │ ├── discord-pre │ │ │ │ └── DiscordPre.ts │ │ │ ├── discord-quote │ │ │ │ └── DiscordQuote.ts │ │ │ ├── discord-reaction │ │ │ │ └── DiscordReaction.ts │ │ │ ├── discord-reactions │ │ │ │ └── DiscordReactions.ts │ │ │ ├── discord-reply │ │ │ │ └── DiscordReply.ts │ │ │ ├── discord-spoiler │ │ │ │ └── DiscordSpoiler.ts │ │ │ ├── discord-string-select-menu-option │ │ │ │ └── DiscordStringSelectMenuOption.ts │ │ │ ├── discord-string-select-menu │ │ │ │ └── DiscordStringSelectMenu.ts │ │ │ ├── discord-subscript │ │ │ │ └── DiscordSubscript.ts │ │ │ ├── discord-system-message │ │ │ │ └── DiscordSystemMessage.ts │ │ │ ├── discord-tenor-video │ │ │ │ └── DiscordTenorVideo.ts │ │ │ ├── discord-thread-message │ │ │ │ └── DiscordThreadMessage.ts │ │ │ ├── discord-thread │ │ │ │ └── DiscordThread.ts │ │ │ ├── discord-time │ │ │ │ └── DiscordTime.ts │ │ │ ├── discord-underlined │ │ │ │ └── DiscordUnderlined.ts │ │ │ ├── discord-unordered-list │ │ │ │ └── DiscordUnorderedList.ts │ │ │ ├── discord-verified-author-tag │ │ │ │ └── DiscordVerifiedAuthorTag.ts │ │ │ ├── discord-video-attachment │ │ │ │ └── DiscordVideoAttachment.ts │ │ │ └── svgs │ │ │ │ ├── AttachmentDownloadButton.ts │ │ │ │ ├── AttachmentReply.ts │ │ │ │ ├── Boost.ts │ │ │ │ ├── ChannelForum.ts │ │ │ │ ├── ChannelIcon.ts │ │ │ │ ├── ChannelThread.ts │ │ │ │ ├── ChannelsAndRoles.ts │ │ │ │ ├── CommandIcon.ts │ │ │ │ ├── CommandIconName.ts │ │ │ │ ├── CommandReply.ts │ │ │ │ ├── CustomizeCommunity.ts │ │ │ │ ├── DMCall.ts │ │ │ │ ├── DMEdit.ts │ │ │ │ ├── DMMissedCall.ts │ │ │ │ ├── Ephemeral.ts │ │ │ │ ├── ExpandMore.ts │ │ │ │ ├── FileAttachment.ts │ │ │ │ ├── GuildBadge.ts │ │ │ │ ├── LaunchIcon.ts │ │ │ │ ├── LockedVoiceChannel.ts │ │ │ │ ├── MediaPauseIcon.ts │ │ │ │ ├── MediaPlayIcon.ts │ │ │ │ ├── MediaRestartIcon.ts │ │ │ │ ├── MediaVolumeAbove50PercentIcon.ts │ │ │ │ ├── MediaVolumeBelow50PercentIcon.ts │ │ │ │ ├── MediaVolumeMutedIcon.ts │ │ │ │ ├── MessageIcon.ts │ │ │ │ ├── ModalClose.ts │ │ │ │ ├── ModalWarning.ts │ │ │ │ ├── PartnerBadgeOverlay.ts │ │ │ │ ├── Pin.ts │ │ │ │ ├── ReplyIcon.ts │ │ │ │ ├── ServerGuide.ts │ │ │ │ ├── ServerUpgrade.ts │ │ │ │ ├── SystemAlert.ts │ │ │ │ ├── SystemError.ts │ │ │ │ ├── Thread.ts │ │ │ │ ├── UserJoin.ts │ │ │ │ ├── UserLeave.ts │ │ │ │ ├── VerifiedBadgeOverlay.ts │ │ │ │ ├── VerifiedTick.ts │ │ │ │ ├── VideoFullScreenIcon.ts │ │ │ │ ├── VideoPausePopIcon.ts │ │ │ │ ├── VideoPlayPopIcon.ts │ │ │ │ ├── VoiceChannel.ts │ │ │ │ └── clan-icons │ │ │ │ ├── Crystal.ts │ │ │ │ ├── Diamond.ts │ │ │ │ ├── Explosion.ts │ │ │ │ ├── Flame.ts │ │ │ │ ├── Flower.ts │ │ │ │ ├── Heart.ts │ │ │ │ ├── Key.ts │ │ │ │ ├── Leaf.ts │ │ │ │ ├── Lightning.ts │ │ │ │ ├── Magic.ts │ │ │ │ ├── Moon.ts │ │ │ │ ├── Mushroom.ts │ │ │ │ ├── Mythical.ts │ │ │ │ ├── Ornament.ts │ │ │ │ ├── Plasma.ts │ │ │ │ ├── Rock.ts │ │ │ │ ├── Shell.ts │ │ │ │ ├── Skull.ts │ │ │ │ ├── Sun.ts │ │ │ │ ├── Sword.ts │ │ │ │ ├── Water.ts │ │ │ │ └── index.ts │ │ ├── config.ts │ │ ├── hex-to-rgba.ts │ │ ├── index.ts │ │ ├── spread.ts │ │ ├── tsconfig.json │ │ ├── types.ts │ │ └── util.ts │ ├── tsconfig.eslint.json │ └── web-dev-server.config.mjs ├── documentation │ ├── .gitignore │ ├── CHANGELOG.md │ ├── astro.config.mts │ ├── package.json │ ├── public │ │ ├── .nojekyll │ │ ├── CNAME │ │ ├── _redirects │ │ ├── browserconfig.xml │ │ ├── favicons │ │ │ ├── android-chrome-144x144.png │ │ │ ├── android-chrome-192x192.png │ │ │ ├── android-chrome-256x256.png │ │ │ ├── android-chrome-36x36.png │ │ │ ├── android-chrome-384x384.png │ │ │ ├── android-chrome-48x48.png │ │ │ ├── android-chrome-72x72.png │ │ │ ├── android-chrome-96x96.png │ │ │ ├── apple-startup.png │ │ │ ├── apple-touch-icon-114x114-precomposed.png │ │ │ ├── apple-touch-icon-114x114.png │ │ │ ├── apple-touch-icon-120x120-precomposed.png │ │ │ ├── apple-touch-icon-120x120.png │ │ │ ├── apple-touch-icon-144x144-precomposed.png │ │ │ ├── apple-touch-icon-144x144.png │ │ │ ├── apple-touch-icon-152x152-precomposed.png │ │ │ ├── apple-touch-icon-152x152.png │ │ │ ├── apple-touch-icon-180x180-precomposed.png │ │ │ ├── apple-touch-icon-180x180.png │ │ │ ├── apple-touch-icon-57x57-precomposed.png │ │ │ ├── apple-touch-icon-57x57.png │ │ │ ├── apple-touch-icon-60x60-precomposed.png │ │ │ ├── apple-touch-icon-60x60.png │ │ │ ├── apple-touch-icon-72x72-precomposed.png │ │ │ ├── apple-touch-icon-72x72.png │ │ │ ├── apple-touch-icon-76x76-precomposed.png │ │ │ ├── apple-touch-icon-76x76.png │ │ │ ├── apple-touch-icon-precomposed.png │ │ │ ├── apple-touch-icon.png │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ ├── favicon.ico │ │ │ ├── mstile-144x144.png │ │ │ ├── mstile-150x150.png │ │ │ ├── mstile-310x150.png │ │ │ ├── mstile-310x310.png │ │ │ ├── mstile-70x70.png │ │ │ ├── opengraph.png │ │ │ └── safari-pinned-tab.svg │ │ ├── manifest.webmanifest │ │ ├── robots.txt │ │ └── screenshots │ │ │ ├── pwa-desktop.png │ │ │ └── pwa-mobile.png │ ├── src │ │ ├── assets │ │ │ ├── README.md │ │ │ ├── discord-components-logo.png │ │ │ └── styles.css │ │ ├── components │ │ │ ├── astro │ │ │ │ ├── framework-grid.astro │ │ │ │ ├── images │ │ │ │ │ ├── angular.svg │ │ │ │ │ ├── astro.svg │ │ │ │ │ ├── docusaurus.svg │ │ │ │ │ ├── htmx.svg │ │ │ │ │ ├── nextjs.svg │ │ │ │ │ ├── nuxt.svg │ │ │ │ │ ├── preact.svg │ │ │ │ │ ├── qwik.svg │ │ │ │ │ ├── react.svg │ │ │ │ │ ├── solid.svg │ │ │ │ │ ├── svelte.svg │ │ │ │ │ ├── sveltekit.svg │ │ │ │ │ ├── vite.svg │ │ │ │ │ └── vue.svg │ │ │ │ └── templates.ts │ │ │ └── lit │ │ │ │ └── DiscordComponentsWrapper.ts │ │ ├── content │ │ │ ├── config.ts │ │ │ └── docs │ │ │ │ ├── index.mdx │ │ │ │ ├── samples.mdx │ │ │ │ └── upgrading │ │ │ │ └── v3.x-v4.x.md │ │ ├── env.d.ts │ │ ├── plugins │ │ │ └── frontmatter.js │ │ └── utils │ │ │ └── seoConfig.ts │ └── tsconfig.json └── react │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── scripts │ └── rename-esm-index.mjs │ ├── src │ ├── index.ts │ ├── react-components │ │ └── createComponent.ts │ └── tsconfig.json │ └── tsup.config.ts ├── scripts ├── inject-readme-markdown.mjs ├── update-core-index-exports.mjs ├── update-exports-and-side-effects.mjs └── update-react-index-exports.mjs ├── tsconfig.base.json ├── tsconfig.eslint.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | 9 | [*.{js,ts}] 10 | indent_size = 4 11 | indent_style = tab 12 | block_comment_start = /* 13 | block_comment = * 14 | block_comment_end = */ 15 | 16 | [*.{yml,yaml}] 17 | indent_size = 2 18 | indent_style = space 19 | 20 | [*.{md,rmd,mkd,mkdn,mdwn,mdown,markdown,litcoffee}] 21 | tab_width = 4 22 | trim_trailing_whitespace = false 23 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @favna -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | **The issue tracker is only for issue reporting or proposals/suggestions. If you 4 | have a question, you can find us in our [Discord Server]**. 5 | 6 | To contribute to this repository, feel free to create a new fork of the 7 | repository and submit a pull request. We highly suggest [ESLint] to be installed 8 | in your text editor or IDE of your choice to ensure builds from GitHub Actions 9 | do not fail. 10 | 11 | 1. Fork, clone, and select the **main** branch. 12 | 2. Create a new branch in your fork. 13 | 3. Make your changes. 14 | 4. Ensure your linting and tests pass by running `yarn lint` 15 | 5. Commit your changes, and push them. 16 | 6. Submit a Pull Request [here]! 17 | 18 | [discord server]: https://join.skyra.pw 19 | [here]: https://github.com/skyra-project/discord-components/pulls 20 | [eslint]: https://eslint.org/ 21 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [kyranet, favna] 4 | patreon: kyranet 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: kyranet 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | custom: ['https://donate.skyra.pw/paypal', 'https://donate.favware.tech/paypal'] 9 | -------------------------------------------------------------------------------- /.github/hooks/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | yarn commitlint --edit $1 -------------------------------------------------------------------------------- /.github/hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | yarn lint-staged 4 | -------------------------------------------------------------------------------- /.github/labels.yml: -------------------------------------------------------------------------------- 1 | - name: packages:core 2 | color: 'fbca04' 3 | - name: packages:react 4 | color: 'fbca04' 5 | - name: resolved in alpha 6 | color: '445F94' 7 | -------------------------------------------------------------------------------- /.github/problemMatchers/eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "eslint-stylish", 5 | "pattern": [ 6 | { 7 | "regexp": "^([^\\s].*)$", 8 | "file": 1 9 | }, 10 | { 11 | "regexp": "^\\s+(\\d+):(\\d+)\\s+(error|warning|info)\\s+(.*)\\s\\s+(.*)$", 12 | "line": 1, 13 | "column": 2, 14 | "severity": 3, 15 | "message": 4, 16 | "code": 5, 17 | "loop": true 18 | } 19 | ] 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /.github/problemMatchers/tsc.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "tsc", 5 | "pattern": [ 6 | { 7 | "regexp": "^(?:\\s+\\d+\\>)?([^\\s].*)\\((\\d+|\\d+,\\d+|\\d+,\\d+,\\d+,\\d+)\\)\\s*:\\s+(error|warning|info)\\s+(\\w{1,2}\\d+)\\s*:\\s*(.*)$", 8 | "file": 1, 9 | "location": 2, 10 | "severity": 3, 11 | "code": 4, 12 | "message": 5 13 | } 14 | ] 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["github>sapphiredev/.github:sapphire-renovate"] 4 | } 5 | -------------------------------------------------------------------------------- /.github/workflows/continuous-delivery.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Delivery 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | concurrency: 9 | group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} 10 | cancel-in-progress: true 11 | 12 | jobs: 13 | publish: 14 | name: Publish Alpha 15 | runs-on: ubuntu-latest 16 | if: github.repository_owner == 'skyra-project' 17 | steps: 18 | - name: Checkout Project 19 | uses: actions/checkout@v4 20 | with: 21 | fetch-depth: 0 22 | ref: main 23 | - name: Install dependencies 24 | uses: sapphiredev/.github/actions/install-yarn-dependencies@main 25 | with: 26 | node-version: 20 27 | - name: Configure Git 28 | run: | 29 | git remote set-url origin "https://${GITHUB_TOKEN}:x-oauth-basic@github.com/${GITHUB_REPOSITORY}.git" 30 | git config --local user.email "${GITHUB_EMAIL}" 31 | git config --local user.name "${GITHUB_USER}" 32 | env: 33 | GITHUB_USER: github-actions[bot] 34 | GITHUB_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com 35 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 36 | - name: Update generated readme files 37 | run: yarn inject 38 | - name: Build code 39 | run: yarn build 40 | - name: Bump Versions and make release 41 | working-directory: packages/core 42 | run: | 43 | yarn bump --preid "alpha.$(git rev-parse --verify --short HEAD)" --skip-changelog --dry-run 44 | env: 45 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 46 | - name: Store new version 47 | id: store-new-version 48 | working-directory: packages/core 49 | run: echo "new_version=$(jq -r '.version' ./package.json)" >> "$GITHUB_OUTPUT" 50 | - name: Set version of react package 51 | working-directory: packages/react 52 | run: yarn version ${{ steps.store-new-version.outputs.new_version }} --immediate 53 | - name: Copy changelog over 54 | working-directory: packages/react 55 | run: cp ../core/CHANGELOG.md CHANGELOG.md 56 | - name: Configure yarn 57 | working-directory: packages/core 58 | run: | 59 | yarn config set npmAuthToken ${NODE_AUTH_TOKEN} 60 | yarn config set npmPublishRegistry "https://registry.yarnpkg.com" 61 | env: 62 | NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }} 63 | - name: Publish @skyra/discord-components-core 64 | working-directory: packages/core 65 | if: false 66 | run: yarn npm publish --tag alpha 67 | - name: Publish @skyra/discord-components-react 68 | working-directory: packages/react 69 | if: false 70 | run: yarn npm publish --tag alpha 71 | -------------------------------------------------------------------------------- /.github/workflows/continuous-integration.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | BuildAndLint: 11 | name: BuildingAndLinting 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout Project 15 | uses: actions/checkout@v4 16 | - name: Add problem matcher 17 | run: | 18 | echo "::add-matcher::.github/problemMatchers/eslint.json" 19 | echo "::add-matcher::.github/problemMatchers/tsc.json" 20 | - name: Use Node.js v20 21 | uses: actions/setup-node@v4 22 | with: 23 | node-version: 20 24 | cache: yarn 25 | - name: Install Dependencies 26 | run: yarn --immutable 27 | - name: Build Code 28 | run: yarn build 29 | - name: Run ESLint 30 | run: yarn lint --fix=false 31 | -------------------------------------------------------------------------------- /.github/workflows/labelsync.yml: -------------------------------------------------------------------------------- 1 | name: Automatic Label Sync 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | workflow_dispatch: 7 | 8 | jobs: 9 | label_sync: 10 | name: Automatic Label Synchronization 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout Core Labels 14 | uses: actions/checkout@v4 15 | with: 16 | path: core-labels 17 | sparse-checkout: .github/labels.yml 18 | sparse-checkout-cone-mode: false 19 | repository: 'sapphiredev/.github' 20 | - name: Checkout Overwrite Labels 21 | uses: actions/checkout@v4 22 | with: 23 | path: overwrite-labels 24 | sparse-checkout: .github/labels.yml 25 | sparse-checkout-cone-mode: false 26 | - name: Merge labels 27 | run: | 28 | yq '. *+ load("core-labels/.github/labels.yml")' overwrite-labels/.github/labels.yml > labels.yml 29 | - name: Run Label Sync 30 | uses: crazy-max/ghaction-github-labeler@v5 31 | with: 32 | github-token: ${{ secrets.GITHUB_TOKEN }} 33 | yaml-file: labels.yml 34 | -------------------------------------------------------------------------------- /.github/workflows/publish-documentation.yml: -------------------------------------------------------------------------------- 1 | name: Deploy documentation 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | 9 | permissions: 10 | contents: read 11 | pages: write 12 | id-token: write 13 | 14 | concurrency: 15 | group: 'pages' 16 | cancel-in-progress: true 17 | 18 | jobs: 19 | deploy: 20 | environment: 21 | name: github-pages 22 | url: ${{ steps.deployment.outputs.page_url }} 23 | runs-on: ubuntu-latest 24 | steps: 25 | - name: Checkout 26 | uses: actions/checkout@v4 27 | - name: Setup Pages 28 | uses: actions/configure-pages@v5 29 | - name: Use Node.js v20 30 | uses: actions/setup-node@v4 31 | with: 32 | node-version: 20 33 | cache: yarn 34 | registry-url: https://registry.yarnpkg.com/ 35 | - name: Install Dependencies 36 | run: yarn --immutable 37 | - name: Build code 38 | run: yarn build 39 | - name: Build docs 40 | run: yarn build:documentation 41 | - name: Upload artifact 42 | uses: actions/upload-pages-artifact@v3 43 | with: 44 | path: packages/documentation/dist 45 | - name: Deploy to GitHub Pages 46 | id: deployment 47 | uses: actions/deploy-pages@v4 48 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | is-alpha: 7 | type: boolean 8 | required: false 9 | default: false 10 | description: Whether to publish an alpha version or not 11 | 12 | concurrency: 13 | group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} 14 | cancel-in-progress: true 15 | 16 | jobs: 17 | publish: 18 | name: Publish 19 | runs-on: ubuntu-latest 20 | if: github.repository_owner == 'skyra-project' 21 | steps: 22 | - name: Checkout Project 23 | uses: actions/checkout@v4 24 | with: 25 | fetch-depth: 0 26 | ref: main 27 | token: ${{ secrets.SKYRA_TOKEN }} 28 | - name: Install dependencies 29 | uses: sapphiredev/.github/actions/install-yarn-dependencies@main 30 | with: 31 | node-version: 24 32 | - name: Configure Git 33 | run: | 34 | git remote set-url origin "https://${GITHUB_TOKEN}:x-oauth-basic@github.com/${GITHUB_REPOSITORY}.git" 35 | git config --local user.email "${GITHUB_EMAIL}" 36 | git config --local user.name "${GITHUB_USER}" 37 | env: 38 | GITHUB_USER: github-actions[bot] 39 | GITHUB_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com 40 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 41 | - name: Update generated readme files 42 | run: yarn inject 43 | - name: Build code 44 | run: yarn build 45 | - name: Bump Versions and make release 46 | working-directory: packages/core 47 | run: | 48 | if [ "${{ github.event.inputs.is-alpha }}" == "true" ]; then 49 | yarn bump --preid alpha --github-release-pre-release 50 | else 51 | yarn bump 52 | fi 53 | env: 54 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 55 | - name: Store new version 56 | id: store-new-version 57 | working-directory: packages/core 58 | run: echo "new_version=$(jq -r '.version' ./package.json)" >> "$GITHUB_OUTPUT" 59 | - name: Set version of react package 60 | working-directory: packages/react 61 | run: yarn version ${{ steps.store-new-version.outputs.new_version }} --immediate 62 | - name: Copy changelog over 63 | working-directory: packages/react 64 | run: cp ../core/CHANGELOG.md CHANGELOG.md 65 | - name: Push changed package.json to github 66 | run: | 67 | git add packages/react/package.json packages/react/CHANGELOG.md 68 | git commit -m "chore(react): update package version to ${{ steps.store-new-version.outputs.new_version }}" 69 | git push origin main 70 | - name: Configure yarn 71 | run: | 72 | yarn config set npmAuthToken ${NODE_AUTH_TOKEN} 73 | yarn config set npmPublishRegistry "https://registry.yarnpkg.com" 74 | env: 75 | NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }} 76 | - name: Publish @skyra/discord-components-core 77 | working-directory: packages/core 78 | run: yarn npm publish 79 | - name: Publish @skyra/discord-components-react 80 | working-directory: packages/react 81 | run: yarn npm publish 82 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules/ 3 | /.pnp 4 | .pnp.js 5 | package-lock.json 6 | 7 | # IDE config 8 | .idea/ 9 | 10 | # Yarn files 11 | .yarn/install-state.gz 12 | .yarn/build-state.yml 13 | 14 | # Misc 15 | *.log 16 | *.tmp 17 | *.tmp.* 18 | .DS_Store 19 | Thumbs.db 20 | .env 21 | 22 | # build 23 | dist/ 24 | coverage/ 25 | 26 | custom-elements.json 27 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | CHANGELOG.md 2 | dist/ 3 | coverage/ 4 | node_modules/ 5 | .yarn/ 6 | *.png 7 | custom-elements.json 8 | -------------------------------------------------------------------------------- /.prettierrc.mjs: -------------------------------------------------------------------------------- 1 | import sapphirePrettierConfig from '@sapphire/prettier-config'; 2 | 3 | export default { 4 | ...sapphirePrettierConfig, 5 | plugins: ['prettier-plugin-astro'], 6 | overrides: [ 7 | ...sapphirePrettierConfig.overrides, 8 | { 9 | files: ['*.md'], 10 | options: { 11 | tabWidth: 2, 12 | useTabs: false, 13 | printWidth: 80, 14 | proseWrap: 'always' 15 | } 16 | }, 17 | { 18 | files: ['*.svg'], 19 | options: { 20 | parser: 'html' 21 | } 22 | } 23 | ] 24 | }; 25 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["esbenp.prettier-vscode", "runem.lit-plugin", "dbaeumer.vscode-eslint"], 3 | "unwantedRecommendations": [] 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.preferences.importModuleSpecifierEnding": "js", 3 | "javascript.preferences.importModuleSpecifierEnding": "js", 4 | "editor.codeActionsOnSave": { 5 | "source.organizeImports": "never" 6 | }, 7 | "eslint.useFlatConfig": true 8 | } 9 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | compressionLevel: mixed 2 | 3 | enableGlobalCache: true 4 | 5 | gitHooksPath: .github/hooks 6 | 7 | nodeLinker: node-modules 8 | 9 | plugins: 10 | - path: .yarn/plugins/@yarnpkg/plugin-git-hooks.cjs 11 | spec: 'https://raw.githubusercontent.com/trufflehq/yarn-plugin-git-hooks/main/bundles/%40yarnpkg/plugin-git-hooks.js' 12 | 13 | yarnPath: .yarn/releases/yarn-4.9.2.cjs 14 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | /Users/favna/workspace/skyraproject/discord-components/packages/core/CHANGELOG.md -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright © `2020` `Aura Román, Jeroen Claassens` 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the “Software”), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /assets/dark_mode/compact_mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/assets/dark_mode/compact_mode.png -------------------------------------------------------------------------------- /assets/dark_mode/normal_conversation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/assets/dark_mode/normal_conversation.png -------------------------------------------------------------------------------- /assets/dark_mode/with_embed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/assets/dark_mode/with_embed.png -------------------------------------------------------------------------------- /assets/iconography/discord-components-apple-startup.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/assets/iconography/discord-components-apple-startup.psd -------------------------------------------------------------------------------- /assets/iconography/discord-components-logo.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/assets/iconography/discord-components-logo.psd -------------------------------------------------------------------------------- /assets/iconography/discord-components-opengraphpsd.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/assets/iconography/discord-components-opengraphpsd.psd -------------------------------------------------------------------------------- /assets/light_mode/compact_mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/assets/light_mode/compact_mode.png -------------------------------------------------------------------------------- /assets/light_mode/normal_conversation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/assets/light_mode/normal_conversation.png -------------------------------------------------------------------------------- /assets/light_mode/with_embed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/assets/light_mode/with_embed.png -------------------------------------------------------------------------------- /assets/readme-templates/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Contributors 4 | 5 | Please make sure to read the [Contributing Guide][contributing] before making a 6 | pull request. 7 | 8 | Thank you to all the people who already contributed to Discord Components! 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /assets/readme-templates/DESCRIPTION.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Description 4 | 5 | Discord message components to easily build and display fake Discord messages on 6 | your webpage. 7 | 8 | **This is an adaptation of [wc-discord-message] from [Danktuary]** 9 | 10 | ### Upgrading guide 11 | 12 | The source code and documentation of this package has been updated for version 13 | 4.x of this package. To find out how to upgrade from v3.x to v4.x, please refer 14 | to the 15 | [upgrading guide](https://discord-components.js.org/upgrading/v3x-v4x/#component-changes) 16 | 17 | 18 | -------------------------------------------------------------------------------- /assets/readme-templates/FEATURES.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Features 4 | 5 | - Design modelled after [Discord](https://discord.com/) itself 6 | - Comfy and compact mode support 7 | - Dark and light themes support 8 | - Set the message author's username, avatar (use defaults or provide your own), 9 | role color, and "bot" tag status 10 | - Display fake user, role, and channel mentions 11 | - Complete embed support 12 | - Uses [Lit Element][lit] to support all browsers and environments 13 | - Simple syntax! 14 | 15 | 16 | -------------------------------------------------------------------------------- /assets/readme-templates/HEADER.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | **Web components to easily build and display fake Discord messages on your 4 | webpages** 5 | 6 | [![License](https://img.shields.io/github/license/skyra-project/discord-components?logo=github&maxAge=3600&style=flat-square)](https://github.com/skyra-project/discord-components/blob/main/LICENSE.md) 7 | [![Patreon](https://img.shields.io/badge/donate-patreon-F96854.svg?logo=patreon)](https://donate.skyra.pw/patreon) 8 | 9 | _Core Package_ 10 | 11 | [![npm](https://img.shields.io/npm/v/@skyra/discord-components-core?color=crimson&logo=npm&style=flat-square&label=@skyra/discord-components-core)](https://www.npmjs.com/package/@skyra/discord-components-core) 12 | [![npm](https://img.shields.io/npm/dt/@skyra/discord-components-core.svg?maxAge=3600&logo=npm)](https://www.npmjs.com/package/@skyra/discord-components-core) 13 | [![npm](https://img.shields.io/bundlephobia/min/@skyra/discord-components-core?label=minified&logo=webpack&maxAge=3600)](https://bundlephobia.com/result?p=@skyra/discord-components-core) 14 | 15 | _React Bindings_ 16 | 17 | [![npm](https://img.shields.io/npm/v/@skyra/discord-components-react?color=crimson&logo=npm&style=flat-square&label=@skyra/discord-components-react)](https://www.npmjs.com/package/@skyra/discord-components-react) 18 | [![npm](https://img.shields.io/npm/dt/@skyra/discord-components-react.svg?maxAge=3600&logo=npm)](https://www.npmjs.com/package/@skyra/discord-components-react) 19 | [![npm](https://img.shields.io/bundlephobia/min/@skyra/discord-components-react?label=minified&logo=webpack&maxAge=3600)](https://bundlephobia.com/result?p=@skyra/discord-components-react) 20 | 21 | [![Support Server](https://discord.com/api/guilds/254360814063058944/embed.png?style=banner2)](https://join.skyra.pw) 22 | 23 | 24 | -------------------------------------------------------------------------------- /assets/readme-templates/SCREENSHOTS.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Screenshots 4 | 5 | ### Dark Mode 6 | 7 | **_A normal conversation_** 8 | 9 | ![](https://raw.githubusercontent.com/skyra-project/discord-components/main/assets/dark_mode/normal_conversation.png) 10 | 11 | **_Compact mode_** 12 | 13 | ![](https://raw.githubusercontent.com/skyra-project/discord-components/main/assets/dark_mode/compact_mode.png) 14 | 15 | **_With an embed_** 16 | 17 | ![](https://raw.githubusercontent.com/skyra-project/discord-components/main/assets/dark_mode/with_embed.png) 18 | 19 | ### Light Mode 20 | 21 | **_A normal conversation_** 22 | 23 | ![](https://raw.githubusercontent.com/skyra-project/discord-components/main/assets/light_mode/normal_conversation.png) 24 | 25 | **_Compact mode_** 26 | 27 | ![](https://raw.githubusercontent.com/skyra-project/discord-components/main/assets/light_mode/compact_mode.png) 28 | 29 | **_With an embed_** 30 | 31 | ![](https://raw.githubusercontent.com/skyra-project/discord-components/main/assets/light_mode/with_embed.png) 32 | 33 | 34 | -------------------------------------------------------------------------------- /packages/core/.cliff-jumperrc.yml: -------------------------------------------------------------------------------- 1 | name: discord-components-core 2 | org: skyra 3 | install: true 4 | packagePath: packages/core 5 | identifierBase: false 6 | tagTemplate: 'v{{new-version}}' 7 | monoRepo: false 8 | pushTag: true 9 | gitRepo: skyra-project/discord-components 10 | gitHostVariant: github 11 | githubRelease: true 12 | githubReleaseLatest: true 13 | githubReleaseNameTemplate: '{{new-version}}' 14 | -------------------------------------------------------------------------------- /packages/core/cliff.toml: -------------------------------------------------------------------------------- 1 | [changelog] 2 | header = """ 3 | # Changelog 4 | 5 | All notable changes to this project will be documented in this file.\n 6 | """ 7 | body = """ 8 | {%- macro remote_url() -%} 9 | https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }} 10 | {%- endmacro -%} 11 | {% if version %}\ 12 | # [{{ version | trim_start_matches(pat="v") }}]\ 13 | {% if previous %}\ 14 | {% if previous.version %}\ 15 | ({{ self::remote_url() }}/compare/{{ previous.version }}...{{ version }})\ 16 | {% else %}\ 17 | ({{ self::remote_url() }}/tree/{{ version }})\ 18 | {% endif %}\ 19 | {% endif %} \ 20 | - ({{ timestamp | date(format="%Y-%m-%d") }}) 21 | {% else %}\ 22 | # [unreleased] 23 | {% endif %}\ 24 | {% for group, commits in commits | group_by(attribute="group") %} 25 | ## {{ group | upper_first }} 26 | {% for commit in commits %} 27 | - {% if commit.scope %}\ 28 | **{{commit.scope}}:** \ 29 | {% endif %}\ 30 | {{ commit.message | upper_first }} ([{{ commit.id | truncate(length=7, end="") }}]({{ self::remote_url() }}/commit/{{ commit.id }}))\ 31 | {% if commit.github.pr_number %} (\ 32 | [#{{ commit.github.pr_number }}]({{ self::remote_url() }}/pull/{{ commit.github.pr_number }}) by @{{ commit.github.username }}) \ 33 | {%- endif %}\ 34 | {% if commit.breaking %}\ 35 | {% for breakingChange in commit.footers %}\ 36 | \n{% raw %} {% endraw %}- 💥 **{{ breakingChange.token }}{{ breakingChange.separator }}** {{ breakingChange.value }}\ 37 | {% endfor %}\ 38 | {% endif %}\ 39 | {% endfor %} 40 | {% endfor %}\n 41 | """ 42 | trim = true 43 | footer = "" 44 | 45 | [git] 46 | conventional_commits = true 47 | filter_unconventional = true 48 | commit_parsers = [ 49 | { message = "^feat", group = "🚀 Features" }, 50 | { message = "^fix", group = "🐛 Bug Fixes" }, 51 | { message = "^docs", group = "📝 Documentation" }, 52 | { message = "^perf", group = "🏃 Performance" }, 53 | { message = "^refactor", group = "🏠 Refactor" }, 54 | { message = "^typings", group = "⌨️ Typings" }, 55 | { message = "^types", group = "⌨️ Typings" }, 56 | { message = ".*deprecated", body = ".*deprecated", group = "🚨 Deprecation" }, 57 | { message = "^revert", skip = true }, 58 | { message = "^style", group = "🪞 Styling" }, 59 | { message = "^test", group = "🧪 Testing" }, 60 | { message = "^chore", skip = true }, 61 | { message = "^ci", skip = true }, 62 | { message = "^build", skip = true }, 63 | { body = ".*security", group = "🛡️ Security" }, 64 | ] 65 | commit_preprocessors = [ 66 | # remove issue numbers from commits 67 | { pattern = '\s\((\w+\s)?#([0-9]+)\)', replace = "" }, 68 | ] 69 | filter_commits = true 70 | tag_pattern = "v[0-9]*" 71 | ignore_tags = "" 72 | topo_order = false 73 | sort_commits = "newest" 74 | 75 | [remote.github] 76 | owner = "skyra-project" 77 | repo = "discord-components" 78 | -------------------------------------------------------------------------------- /packages/core/demo/index.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Whitney'; 3 | src: url('https://cdn.skyra.pw/whitney-font/v2/Book.woff') format('woff'); 4 | font-weight: 400; 5 | } 6 | 7 | @font-face { 8 | font-family: 'Whitney'; 9 | src: url('https://cdn.skyra.pw/whitney-font/v2/Medium.woff') format('woff'); 10 | font-weight: 500; 11 | } 12 | 13 | @font-face { 14 | font-family: 'Whitney'; 15 | src: url('https://cdn.skyra.pw/whitney-font/v2/Semibold.woff') format('woff'); 16 | font-weight: 600; 17 | } 18 | 19 | @font-face { 20 | font-family: 'Whitney'; 21 | src: url('https://cdn.skyra.pw/whitney-font/v2/Bold.woff') format('woff'); 22 | font-weight: 700; 23 | } 24 | 25 | body { 26 | color: #eee; 27 | background-color: #2a2a2a; 28 | font-family: Arial, Helvetica, sans-serif; 29 | } 30 | 31 | .body-wrapper { 32 | margin-left: 1rem; 33 | margin-right: 1rem; 34 | } 35 | 36 | .logo { 37 | display: inline-block; 38 | font-weight: bold; 39 | font-size: 2em; 40 | margin: 0; 41 | } 42 | 43 | .title { 44 | border-bottom: 1px solid #4a4a4a; 45 | padding-bottom: 0.25em; 46 | } 47 | 48 | .subtitle { 49 | padding-bottom: 0.125em; 50 | } 51 | 52 | .link { 53 | color: #008080; 54 | text-decoration: none; 55 | } 56 | 57 | .link:hover { 58 | text-decoration: underline; 59 | } 60 | -------------------------------------------------------------------------------- /packages/core/demo/index.js: -------------------------------------------------------------------------------- 1 | globalThis.$discordMessage = { 2 | profiles: { 3 | marciel404: { 4 | author: 'marciel404', 5 | avatar: 'https://github.com/eumarciel404.png', 6 | roleColor: '#00ffff' 7 | }, 8 | maximillian: { 9 | author: 'Maximillian Osborn', 10 | avatar: 'https://raw.githubusercontent.com/skyra-project/discord-components-implementations/main/shared/public/avaone.png', 11 | roleColor: '#f9d61b' 12 | }, 13 | willard: { 14 | author: 'Willard Walton', 15 | avatar: 'https://raw.githubusercontent.com/skyra-project/discord-components-implementations/main/shared/public/avatwo.png', 16 | roleColor: '#ffb12f' 17 | }, 18 | skyra: { 19 | author: 'Skyra', 20 | avatar: 'https://github.com/NM-EEA-Y.png', 21 | roleColor: '#1e88e5', 22 | bot: true, 23 | verified: true 24 | }, 25 | favna: { 26 | author: 'Favna', 27 | avatar: 'https://github.com/favna.png', 28 | roleColor: '#a155ab', 29 | roleIcon: 'https://raw.githubusercontent.com/skyra-project/discord-components-implementations/main/shared/public/booster.png', 30 | roleName: 'Booster' 31 | }, 32 | dominik: { 33 | author: 'Dominik', 34 | avatar: 'https://github.com/mezotv.png', 35 | clanIcon: 'leaf', 36 | clanTag: 'Leaf' 37 | }, 38 | paulos: { 39 | author: 'Paulos', 40 | avatar: 'https://github.com/dpaulos6.png', 41 | clanIcon: 'water', 42 | clanTag: 'CSS3' 43 | }, 44 | discordjs: { 45 | author: 'Discord.js Official #announcements', 46 | avatar: 'https://raw.githubusercontent.com/skyra-project/discord-components-implementations/main/shared/public/discordjs.png', 47 | roleColor: '#ffffff', 48 | server: true 49 | }, 50 | communityupdates: { 51 | author: 'Community Updates', 52 | avatar: 'https://cdn.discordapp.com/avatars/669627189624307712/a0389d52d24fdef878aca87e8d52cc2a.webp', 53 | officialApp: true 54 | } 55 | }, 56 | emojis: { 57 | diamond: { 58 | name: 'diamond', 59 | url: 'https://raw.githubusercontent.com/skyra-project/discord-components-implementations/main/shared/public/diamond.png' 60 | }, 61 | dragonite: { 62 | name: 'dragonite', 63 | url: 'https://raw.githubusercontent.com/skyra-project/discord-components-implementations/main/shared/public/dragonite.png' 64 | }, 65 | sapphire: { 66 | name: 'sapphire', 67 | url: 'https://raw.githubusercontent.com/skyra-project/discord-components-implementations/main/shared/public/sapphire.png' 68 | } 69 | } 70 | }; 71 | -------------------------------------------------------------------------------- /packages/core/src/components/_private/DiscordMediaAttachmentStyles.ts: -------------------------------------------------------------------------------- 1 | import { css } from 'lit'; 2 | 3 | /** 4 | * @internal 5 | */ 6 | export const DiscordMediaAttachmentStyles = css` 7 | .discord-media-attachment-non-visual-media-item-container { 8 | justify-self: start; 9 | align-self: start; 10 | margin-top: 8px; 11 | max-width: 100%; 12 | display: flex; 13 | flex-direction: column; 14 | position: relative; 15 | } 16 | 17 | .discord-media-attachment-mosaic-item-media { 18 | border-radius: 2px; 19 | display: flex; 20 | flex-flow: row nowrap; 21 | height: 100%; 22 | max-height: inherit; 23 | max-width: 100%; 24 | position: relative; 25 | } 26 | 27 | .discord-media-attachment-controls { 28 | width: 100%; 29 | display: flex; 30 | align-items: center; 31 | margin-top: 4px; 32 | background-color: hsl(0 calc(1 * 0%) 0% / 0.6); 33 | border-radius: 3px; 34 | } 35 | 36 | .discord-media-attachment-video-button { 37 | margin-right: 8px; 38 | } 39 | 40 | .discord-media-attachment-control-icon { 41 | display: block; 42 | width: 24px; 43 | height: 24px; 44 | padding: 4px; 45 | cursor: pointer; 46 | flex: 0 0 auto; 47 | opacity: 0.6; 48 | } 49 | 50 | .discord-media-attachment-duration-time-wrapper { 51 | flex: 0 0 auto; 52 | margin: 4px; 53 | height: 12px; 54 | } 55 | 56 | .discord-media-attachment-duration-time-display { 57 | font-weight: 500; 58 | display: inline-block; 59 | font-family: 60 | 'gg mono', 'Source Code Pro', Consolas, 'Andale Mono WT', 'Andale Mono', 'Lucida Console', 'Lucida Sans Typewriter', 'DejaVu Sans Mono', 61 | 'Bitstream Vera Sans Mono', 'Liberation Mono', 'Nimbus Mono L', Monaco, 'Courier New', Courier, monospace; 62 | font-size: 12px; 63 | line-height: 12px; 64 | vertical-align: text-top; 65 | } 66 | 67 | .discord-media-attachment-duration-time-separator { 68 | margin: 0 2px; 69 | } 70 | 71 | .discord-media-attachment-non-visual-media-item-container:hover .discord-button-download-attachment { 72 | display: block !important; 73 | } 74 | 75 | .discord-button-download-attachment { 76 | display: none; 77 | position: absolute; 78 | top: -8px; 79 | right: -8px; 80 | border-radius: 5px; 81 | outline: color-mix(in oklab, hsl(220 calc(1 * 6.5%) 18% / 1) 100%, black 0%); 82 | background-color: color-mix(in oklab, hsl(223 calc(1 * 6.7%) 20.6% / 1) 100%, black 0%); 83 | } 84 | 85 | .discord-link-download-attachment { 86 | color: color-mix(in oklab, hsl(215 calc(1 * 8.8%) 73.3% / 1) 100%, black 0%); 87 | display: flex; 88 | } 89 | 90 | .discord-icon-download { 91 | padding: 6px; 92 | } 93 | `; 94 | -------------------------------------------------------------------------------- /packages/core/src/components/discord-action-row/DiscordActionRow.ts: -------------------------------------------------------------------------------- 1 | import { css, html, LitElement } from 'lit'; 2 | import { customElement } from 'lit/decorators.js'; 3 | 4 | @customElement('discord-action-row') 5 | export class DiscordActionRow extends LitElement { 6 | /** 7 | * @internal 8 | */ 9 | public static override readonly styles = css` 10 | :host { 11 | display: flex; 12 | flex-wrap: wrap; 13 | } 14 | `; 15 | 16 | protected override render() { 17 | return html``; 18 | } 19 | } 20 | 21 | declare global { 22 | interface HTMLElementTagNameMap { 23 | 'discord-action-row': DiscordActionRow; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/core/src/components/discord-attachments/DiscordAttachments.ts: -------------------------------------------------------------------------------- 1 | import { css, html, LitElement } from 'lit'; 2 | import { customElement } from 'lit/decorators.js'; 3 | 4 | @customElement('discord-attachments') 5 | export class DiscordAttachments extends LitElement { 6 | /** 7 | * @internal 8 | */ 9 | public static override readonly styles = css` 10 | :host { 11 | display: grid; 12 | grid-auto-flow: row; 13 | grid-row-gap: 0.25rem; 14 | text-indent: 0; 15 | min-height: 0; 16 | min-width: 0; 17 | padding-top: 0.125rem; 18 | padding-bottom: 0.125rem; 19 | position: relative; 20 | } 21 | 22 | :host > * { 23 | justify-self: start; 24 | -ms-flex-item-align: start; 25 | align-self: start; 26 | } 27 | `; 28 | 29 | protected override render() { 30 | return html``; 31 | } 32 | } 33 | 34 | declare global { 35 | interface HTMLElementTagNameMap { 36 | 'discord-attachments': DiscordAttachments; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/core/src/components/discord-bold/DiscordBold.ts: -------------------------------------------------------------------------------- 1 | import { LitElement, css, html } from 'lit'; 2 | import { customElement } from 'lit/decorators.js'; 3 | 4 | @customElement('discord-bold') 5 | export class DiscordBold extends LitElement { 6 | /** 7 | * @internal 8 | */ 9 | public static override readonly styles = css` 10 | :host > strong { 11 | font-weight: 700; 12 | } 13 | `; 14 | 15 | protected override render() { 16 | return html` 17 | 18 | 19 | 20 | `; 21 | } 22 | } 23 | 24 | declare global { 25 | interface HTMLElementTagNameMap { 26 | 'discord-bold': DiscordBold; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/core/src/components/discord-code/DiscordCode.ts: -------------------------------------------------------------------------------- 1 | import { consume } from '@lit/context'; 2 | import { css, html, LitElement } from 'lit'; 3 | import { customElement, property } from 'lit/decorators.js'; 4 | import type { LightTheme } from '../../types.js'; 5 | import { messagesLightTheme } from '../discord-messages/DiscordMessages.js'; 6 | 7 | @customElement('discord-code') 8 | export class DiscordCode extends LitElement implements LightTheme { 9 | /** 10 | * @internal 11 | */ 12 | public static override readonly styles = css` 13 | :host { 14 | background-color: #2f3136; 15 | white-space: break-spaces; 16 | font-family: 17 | Consolas, 18 | Andale Mono WT, 19 | Andale Mono, 20 | Lucida Console, 21 | Lucida Sans Typewriter, 22 | DejaVu Sans Mono, 23 | Bitstream Vera Sans Mono, 24 | Liberation Mono, 25 | Nimbus Mono L, 26 | Monaco, 27 | Courier New, 28 | Courier, 29 | monospace; 30 | border-radius: 3px; 31 | } 32 | 33 | code { 34 | padding: 0.2em; 35 | margin: -0.2em; 36 | border-radius: 3px; 37 | border: none; 38 | font-size: 85%; 39 | text-indent: 0; 40 | white-space: pre-wrap; 41 | } 42 | 43 | :host([multiline]) code { 44 | display: block; 45 | width: 90%; 46 | font-size: 0.875rem; 47 | line-height: 1.125rem; 48 | padding: 0.5em; 49 | background: #2b2d31; 50 | border: 1px solid #1e1f22; 51 | } 52 | 53 | :host([embed]) code { 54 | background-color: #1e1f22; 55 | } 56 | 57 | :host([embed][multiline]) code { 58 | display: block; 59 | width: 100%; 60 | padding: 7px; 61 | border-radius: 4px; 62 | background: #1e1f22; 63 | } 64 | 65 | :host([light-theme]) code { 66 | border-color: #e3e5e8; 67 | background-color: #f2f3f5; 68 | } 69 | 70 | :host([light-theme][embed]) code { 71 | background-color: #e3e5e8; 72 | } 73 | `; 74 | 75 | /** 76 | * Whether this code block is a multi-line code block 77 | */ 78 | @property({ type: Boolean, reflect: true }) 79 | public accessor multiline = false; 80 | 81 | @consume({ context: messagesLightTheme, subscribe: true }) 82 | @property({ type: Boolean, reflect: true, attribute: 'light-theme' }) 83 | public accessor lightTheme = false; 84 | 85 | /** 86 | * Whether this code block exists within a `discord-embed` component. 87 | */ 88 | @property({ type: Boolean, reflect: true }) 89 | public accessor embed = false; 90 | 91 | protected override render() { 92 | if (this.multiline) { 93 | return html``; 96 | } 97 | 98 | return html``; 99 | } 100 | } 101 | 102 | declare global { 103 | interface HTMLElementTagNameMap { 104 | 'discord-code': DiscordCode; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /packages/core/src/components/discord-embed-description/DiscordEmbedDescription.ts: -------------------------------------------------------------------------------- 1 | import { css, html, LitElement } from 'lit'; 2 | import { customElement } from 'lit/decorators.js'; 3 | 4 | @customElement('discord-embed-description') 5 | export class DiscordEmbedDescription extends LitElement { 6 | /** 7 | * @internal 8 | */ 9 | public static override readonly styles = css` 10 | :host { 11 | font-size: 0.875rem; 12 | font-weight: 400; 13 | grid-column: 1/1; 14 | line-height: 1.125rem; 15 | margin-top: 8px; 16 | min-width: 0; 17 | } 18 | `; 19 | 20 | protected override render() { 21 | return html``; 22 | } 23 | } 24 | 25 | declare global { 26 | interface HTMLElementTagNameMap { 27 | 'discord-embed-description': DiscordEmbedDescription; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/core/src/components/discord-embed-fields/DiscordEmbedFields.ts: -------------------------------------------------------------------------------- 1 | import { css, html, LitElement } from 'lit'; 2 | import { customElement } from 'lit/decorators.js'; 3 | 4 | @customElement('discord-embed-fields') 5 | export class DiscordEmbedFields extends LitElement { 6 | /** 7 | * @internal 8 | */ 9 | public static override readonly styles = css` 10 | :host { 11 | display: grid; 12 | grid-column: 1/1; 13 | margin-top: 8px; 14 | grid-gap: 8px; 15 | } 16 | 17 | ::slotted([inline-index='1']) { 18 | grid-column: 1/5 !important; 19 | } 20 | 21 | ::slotted([inline-index='2']) { 22 | grid-column: 5/9 !important; 23 | } 24 | 25 | ::slotted([inline-index='3']) { 26 | grid-column: 9/13 !important; 27 | } 28 | `; 29 | 30 | protected override render() { 31 | return html``; 32 | } 33 | } 34 | 35 | declare global { 36 | interface HTMLElementTagNameMap { 37 | 'discord-embed-fields': DiscordEmbedFields; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/core/src/components/discord-embed-footer/DiscordEmbedFooter.ts: -------------------------------------------------------------------------------- 1 | import { consume } from '@lit/context'; 2 | import { css, html, LitElement } from 'lit'; 3 | import { customElement, property } from 'lit/decorators.js'; 4 | import { ifDefined } from 'lit/directives/if-defined.js'; 5 | import { when } from 'lit/directives/when.js'; 6 | import type { DiscordTimestamp, LightTheme } from '../../types.js'; 7 | import { handleTimestamp } from '../../util.js'; 8 | import { messagesLightTheme } from '../discord-messages/DiscordMessages.js'; 9 | 10 | @customElement('discord-embed-footer') 11 | export class DiscordEmbedFooter extends LitElement implements LightTheme { 12 | /** 13 | * @internal 14 | */ 15 | public static override readonly styles = css` 16 | :host { 17 | -webkit-box-align: center; 18 | align-items: center; 19 | display: flex; 20 | font-size: 12px; 21 | line-height: 16px; 22 | font-weight: 500; 23 | margin-top: 8px; 24 | } 25 | 26 | :host([light-theme]) { 27 | color: #747f8d; 28 | } 29 | 30 | :host .discord-footer-image { 31 | border-radius: 50%; 32 | flex-shrink: 0; 33 | height: 20px; 34 | margin-right: 8px; 35 | width: 20px; 36 | } 37 | 38 | :host .discord-footer-separator { 39 | color: #dcddde; 40 | font-weight: 500; 41 | display: inline-block; 42 | margin: 0 4px; 43 | } 44 | 45 | :host([light-theme]) .discord-footer-separator { 46 | color: #5c5e66; 47 | } 48 | `; 49 | 50 | /** 51 | * The image to use next to the footer text. 52 | */ 53 | @property({ attribute: 'footer-image' }) 54 | public accessor footerImage: string; 55 | 56 | /** 57 | * The alt attribute to use for the {@link DiscordEmbedFooter.footerImage} 58 | */ 59 | @property({ attribute: 'footer-image-alt' }) 60 | public accessor footerImageAlt: string; 61 | 62 | /** 63 | * The timestamp to use for the message date. When supplying a string, the format must be `01/31/2000`. 64 | */ 65 | @property({ 66 | type: String, 67 | reflect: true, 68 | converter: (value) => handleTimestamp(value), 69 | attribute: true 70 | }) 71 | public accessor timestamp: DiscordTimestamp; 72 | 73 | @consume({ context: messagesLightTheme, subscribe: true }) 74 | @property({ type: Boolean, reflect: true, attribute: 'light-theme' }) 75 | public accessor lightTheme = false; 76 | 77 | public updateTimestamp(value?: DiscordTimestamp): void { 78 | if (value && !Number.isNaN(new Date(value).getTime())) { 79 | this.timestamp = handleTimestamp(value); 80 | } 81 | } 82 | 83 | protected override render() { 84 | this.updateTimestamp(this.timestamp); 85 | 86 | return html`${when( 87 | this.footerImage, 88 | () => html`${ifDefined(this.footerImageAlt)}` 89 | )} 90 | 91 | ${when(this.timestamp, () => html``)} 92 | ${when( 93 | this.timestamp, 94 | () => ` ${this.timestamp}`, 95 | () => null 96 | )}`; 97 | } 98 | } 99 | 100 | declare global { 101 | interface HTMLElementTagNameMap { 102 | 'discord-embed-footer': DiscordEmbedFooter; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /packages/core/src/components/discord-header/DiscordHeader.ts: -------------------------------------------------------------------------------- 1 | import { css, html, LitElement } from 'lit'; 2 | import { customElement, property } from 'lit/decorators.js'; 3 | import { choose } from 'lit/directives/choose.js'; 4 | 5 | @customElement('discord-header') 6 | export class DiscordHeader extends LitElement { 7 | /** 8 | * @internal 9 | */ 10 | public static override readonly styles = css` 11 | :host > * { 12 | margin: 16px 0 8px; 13 | font-weight: 700; 14 | line-height: 1.375em; 15 | } 16 | 17 | :host([level='1']) > h1 { 18 | font-size: 1.5rem; 19 | } 20 | 21 | :host([level='2']) > h2 { 22 | font-size: 1.25rem; 23 | } 24 | 25 | :host([level='3']) > h3 { 26 | font-size: 1rem; 27 | } 28 | 29 | :host([level='1']):first-child() > h1, 30 | :host([level='2']):first-child() > h2 { 31 | margin-top: 8px; 32 | } 33 | 34 | :host([level='3']):first-child() > h3 { 35 | margin-top: 4px; 36 | } 37 | `; 38 | 39 | /** 40 | * The header level, this should be a number between 1 and 3 (inclusive). 41 | * If a number outside of this range is provided, an error is thrown. 42 | */ 43 | @property({ type: Number, reflect: true }) 44 | public accessor level: 1 | 2 | 3 = 1; 45 | 46 | public ensureLevelIsNumber(): void { 47 | if (this.level && !Number.isNaN(this.level)) { 48 | this.level = Number(this.level) as typeof this.level; 49 | } 50 | } 51 | 52 | public checkLevel() { 53 | if (this.level < 1 || this.level > 3) { 54 | throw new RangeError('The level property must be a number between 1 and 3 (inclusive)'); 55 | } 56 | } 57 | 58 | protected override render() { 59 | this.ensureLevelIsNumber(); 60 | this.checkLevel(); 61 | 62 | return choose( 63 | this.level, 64 | [ 65 | [1, () => html`

`], 66 | [2, () => html`

`], 67 | [3, () => html`

`] 68 | ], 69 | () => html`` 70 | ); 71 | } 72 | } 73 | 74 | declare global { 75 | interface HTMLElementTagNameMap { 76 | 'discord-header': DiscordHeader; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /packages/core/src/components/discord-italic/DiscordItalic.ts: -------------------------------------------------------------------------------- 1 | import { css, html, LitElement } from 'lit'; 2 | import { customElement } from 'lit/decorators.js'; 3 | 4 | @customElement('discord-italic') 5 | export class DiscordItalic extends LitElement { 6 | /** 7 | * @internal 8 | */ 9 | public static override readonly styles = css` 10 | :host > em { 11 | font-style: italic; 12 | } 13 | `; 14 | 15 | protected override render() { 16 | return html` 17 | 18 | 19 | 20 | `; 21 | } 22 | } 23 | 24 | declare global { 25 | interface HTMLElementTagNameMap { 26 | 'discord-italic': DiscordItalic; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/core/src/components/discord-link/DiscordLink.ts: -------------------------------------------------------------------------------- 1 | import { consume } from '@lit/context'; 2 | import { css, html, LitElement } from 'lit'; 3 | import { customElement, property } from 'lit/decorators.js'; 4 | import { classMap } from 'lit/directives/class-map.js'; 5 | import { ifDefined } from 'lit/directives/if-defined.js'; 6 | import type { LightTheme } from '../../types.js'; 7 | import { messagesLightTheme } from '../discord-messages/DiscordMessages.js'; 8 | 9 | @customElement('discord-link') 10 | export class DiscordLink extends LitElement implements LightTheme { 11 | /** 12 | * @internal 13 | */ 14 | public static override readonly styles = css` 15 | a { 16 | color: #00aff4; 17 | text-decoration: none; 18 | } 19 | 20 | a:hover { 21 | text-decoration: underline; 22 | } 23 | 24 | .discord-link-light-theme a { 25 | color: #00b0f4; 26 | } 27 | `; 28 | 29 | @consume({ context: messagesLightTheme, subscribe: true }) 30 | @property({ type: Boolean, reflect: true, attribute: 'light-theme' }) 31 | public accessor lightTheme = false; 32 | 33 | /** 34 | * The URL to link 35 | * 36 | * @example 37 | * ```ts 38 | * 'https://example.com/example.txt' 39 | * ``` 40 | */ 41 | @property() 42 | public accessor href: string; 43 | 44 | /** 45 | * The `` tag {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#rel | `rel`} 46 | */ 47 | @property() 48 | public accessor rel: string; 49 | 50 | /** 51 | * The `` tag {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#target | `target`} 52 | */ 53 | @property() 54 | public accessor target: '_blank' | '_parent' | '_self' | '_top'; 55 | 56 | /** 57 | * The `` tag {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#type | `type`} 58 | */ 59 | @property() 60 | public accessor type: string; 61 | 62 | protected override render() { 63 | return html``; 71 | } 72 | } 73 | 74 | declare global { 75 | interface HTMLElementTagNameMap { 76 | 'discord-link': DiscordLink; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /packages/core/src/components/discord-list-item/DiscordListItem.ts: -------------------------------------------------------------------------------- 1 | import { css, html, LitElement } from 'lit'; 2 | import { customElement } from 'lit/decorators.js'; 3 | import { DiscordComponentsError } from '../../util.js'; 4 | 5 | @customElement('discord-list-item') 6 | export class DiscordListItem extends LitElement { 7 | /** 8 | * @internal 9 | */ 10 | public static override readonly styles = css` 11 | :host > li { 12 | margin-bottom: 4px; 13 | } 14 | `; 15 | 16 | public checkParentElement() { 17 | if ( 18 | this.parentElement?.tagName.toLowerCase() !== 'discord-unordered-list' && 19 | this.parentElement?.tagName.toLowerCase() !== 'discord-ordered-list' 20 | ) { 21 | throw new DiscordComponentsError( 22 | 'All components must be direct children of or .' 23 | ); 24 | } 25 | } 26 | 27 | protected override render() { 28 | this.checkParentElement(); 29 | return html`
  • `; 30 | } 31 | } 32 | 33 | declare global { 34 | interface HTMLElementTagNameMap { 35 | 'discord-list-item': DiscordListItem; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/core/src/components/discord-ordered-list/DiscordOrderedList.ts: -------------------------------------------------------------------------------- 1 | import { css, html, LitElement } from 'lit'; 2 | import { customElement, property, state } from 'lit/decorators.js'; 3 | import { DiscordComponentsError } from '../../util.js'; 4 | 5 | @customElement('discord-ordered-list') 6 | export class DiscordOrderedList extends LitElement { 7 | /** 8 | * @internal 9 | */ 10 | public static override readonly styles = css` 11 | :host > ol { 12 | list-style-image: initial; 13 | list-style-type: decimal; 14 | list-style-position: outside; 15 | margin-bottom: 0px; 16 | margin-top: 4px; 17 | margin-right: 0px; 18 | margin-left: calc(0.4em + 0.6em * var(--totalCharacters)); 19 | padding: 0px; 20 | } 21 | `; 22 | 23 | /** 24 | * The starting number for the ordered list. 25 | * 26 | * You can set this to start the list at a specific number 27 | * 28 | * @defaultValue 1 29 | */ 30 | @property({ type: Number, reflect: true }) 31 | public accessor start = 1; 32 | 33 | @state() 34 | private accessor startLength = 1; 35 | 36 | public checkChildren() { 37 | const allChildrenAreListItems = Array.from(this.children).every((child) => { 38 | const tagNameLowerCase = child.tagName.toLowerCase(); 39 | return ( 40 | tagNameLowerCase === 'discord-list-item' || 41 | tagNameLowerCase === 'discord-unordered-list' || 42 | tagNameLowerCase === 'discord-ordered-list' 43 | ); 44 | }); 45 | 46 | if (!allChildrenAreListItems) { 47 | throw new DiscordComponentsError( 48 | 'All direct children inside of a components must be one of , , or .' 49 | ); 50 | } 51 | } 52 | 53 | /** 54 | * Checks how many children of `` this component has and updates 55 | * the {@link DiscordOrderedList.startLength | startLength} state. 56 | */ 57 | protected override willUpdate(): void { 58 | const amountOfListItems = Array.from(this.children).filter((child) => { 59 | return child.tagName.toLowerCase() === 'discord-list-item'; 60 | }).length; 61 | 62 | const totalCount = this.start + amountOfListItems; 63 | 64 | this.startLength = totalCount.toString().length; 65 | } 66 | 67 | protected override render() { 68 | this.checkChildren(); 69 | 70 | // We disable the eslint rule here because users should use the component inside of this component. 71 | // eslint-disable-next-line lit-a11y/list 72 | return html`
      73 | 74 |
    `; 75 | } 76 | } 77 | 78 | declare global { 79 | interface HTMLElementTagNameMap { 80 | 'discord-ordered-list': DiscordOrderedList; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /packages/core/src/components/discord-pre/DiscordPre.ts: -------------------------------------------------------------------------------- 1 | import { css, html, LitElement } from 'lit'; 2 | import { customElement, property } from 'lit/decorators.js'; 3 | 4 | @customElement('discord-pre') 5 | export class DiscordPre extends LitElement { 6 | /** 7 | * @internal 8 | */ 9 | public static override readonly styles = css` 10 | :host pre { 11 | border-radius: 4px; 12 | padding: 0; 13 | font-size: 0.75rem; 14 | line-height: 1rem; 15 | margin-top: 6px; 16 | white-space: pre-wrap; 17 | background-clip: border-box; 18 | width: 90%; 19 | border: none; 20 | } 21 | 22 | :host([embed]) pre { 23 | margin: 0; 24 | margin-top: 6px; 25 | width: 100%; 26 | } 27 | `; 28 | 29 | @property({ type: Boolean, reflect: true }) 30 | public accessor embed = false; 31 | 32 | protected override render() { 33 | return html`
    `; 35 | } 36 | } 37 | 38 | declare global { 39 | interface HTMLElementTagNameMap { 40 | 'discord-pre': DiscordPre; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/core/src/components/discord-quote/DiscordQuote.ts: -------------------------------------------------------------------------------- 1 | import { consume } from '@lit/context'; 2 | import { css, html, LitElement } from 'lit'; 3 | import { customElement, property } from 'lit/decorators.js'; 4 | import type { LightTheme } from '../../types.js'; 5 | import { messagesLightTheme } from '../discord-messages/DiscordMessages.js'; 6 | 7 | @customElement('discord-quote') 8 | export class DiscordQuote extends LitElement implements LightTheme { 9 | /** 10 | * @internal 11 | */ 12 | public static override readonly styles = css` 13 | :host { 14 | display: flex; 15 | } 16 | 17 | .discord-quote-divider { 18 | background-color: #4f545c; 19 | border-radius: 4px; 20 | font-size: 0.9em; 21 | font-style: normal; 22 | font-weight: 400; 23 | margin: 0; 24 | padding: 0; 25 | width: 4px; 26 | } 27 | 28 | :host([light-theme]) .discord-quote-divider { 29 | background-color: #c4c9ce; 30 | } 31 | 32 | blockquote { 33 | margin-block-end: unset; 34 | margin-block-start: unset; 35 | margin-inline-end: unset; 36 | margin-inline-start: unset; 37 | padding: 0 8px 0 12px; 38 | } 39 | `; 40 | 41 | @consume({ context: messagesLightTheme, subscribe: true }) 42 | @property({ type: Boolean, reflect: true, attribute: 'light-theme' }) 43 | public accessor lightTheme = false; 44 | 45 | protected override render() { 46 | return html` 47 |
    48 | 49 |
    50 | `; 51 | } 52 | } 53 | 54 | declare global { 55 | interface HTMLElementTagNameMap { 56 | 'discord-quote': DiscordQuote; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /packages/core/src/components/discord-reactions/DiscordReactions.ts: -------------------------------------------------------------------------------- 1 | import { css, html, LitElement } from 'lit'; 2 | import { customElement } from 'lit/decorators.js'; 3 | 4 | @customElement('discord-reactions') 5 | export class DiscordReactions extends LitElement { 6 | /** 7 | * @internal 8 | */ 9 | public static override readonly styles = css` 10 | :host { 11 | display: flex; 12 | -webkit-box-flex: 1; 13 | -ms-flex: 1 0 auto; 14 | flex: 1 0 auto; 15 | align-items: center; 16 | flex-wrap: wrap; 17 | } 18 | `; 19 | 20 | protected override render() { 21 | return html``; 22 | } 23 | } 24 | 25 | declare global { 26 | interface HTMLElementTagNameMap { 27 | 'discord-reactions': DiscordReactions; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/core/src/components/discord-spoiler/DiscordSpoiler.ts: -------------------------------------------------------------------------------- 1 | import { consume } from '@lit/context'; 2 | import { css, html, LitElement } from 'lit'; 3 | import { customElement, property } from 'lit/decorators.js'; 4 | import type { LightTheme } from '../../types.js'; 5 | import { messagesLightTheme } from '../discord-messages/DiscordMessages.js'; 6 | 7 | @customElement('discord-spoiler') 8 | export class DiscordSpoiler extends LitElement implements LightTheme { 9 | /** 10 | * @internal 11 | */ 12 | public static override readonly styles = css` 13 | :host { 14 | background-color: #202225; 15 | border-radius: 3px; 16 | color: transparent; 17 | cursor: pointer; 18 | } 19 | 20 | :host([light-theme]) { 21 | background-color: #c4c9ce; 22 | } 23 | 24 | :host(:hover) { 25 | background-color: rgba(32, 34, 37, 0.8); 26 | } 27 | 28 | :host([light-theme]:hover) { 29 | background-color: #cfd3d7; 30 | } 31 | 32 | :host([activated]) { 33 | color: inherit; 34 | background-color: hsla(0, 0%, 100%, 0.1); 35 | } 36 | 37 | :host([light-theme][activated]) { 38 | background-color: #e5e5e5; 39 | } 40 | `; 41 | 42 | @property({ type: Boolean, reflect: true }) 43 | public accessor activated = false; 44 | 45 | /** 46 | * Whether to use light theme or not. 47 | */ 48 | @consume({ context: messagesLightTheme }) 49 | @property({ type: Boolean, reflect: true, attribute: 'light-theme' }) 50 | public accessor lightTheme = false; 51 | 52 | protected override render() { 53 | return html` { 55 | this.activated = true; 56 | }} 57 | @keydown=${() => { 58 | this.activated = true; 59 | }} 60 | >`; 61 | } 62 | } 63 | 64 | declare global { 65 | interface HTMLElementTagNameMap { 66 | 'discord-spoiler': DiscordSpoiler; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /packages/core/src/components/discord-subscript/DiscordSubscript.ts: -------------------------------------------------------------------------------- 1 | import { consume } from '@lit/context'; 2 | import { LitElement, css, html } from 'lit'; 3 | import { customElement, property } from 'lit/decorators.js'; 4 | import type { LightTheme } from '../../types.js'; 5 | import { messagesLightTheme } from '../discord-messages/DiscordMessages.js'; 6 | 7 | @customElement('discord-subscript') 8 | export class DiscordSubscript extends LitElement implements LightTheme { 9 | /** 10 | * @internal 11 | */ 12 | public static override readonly styles = css` 13 | small { 14 | display: block; 15 | color: color-mix(in oklab, hsl(214 calc(1 * 8.1%) 61.2% / 1) 100%, black 0%); 16 | font-size: 0.8125rem; 17 | line-height: 1.11719rem; 18 | } 19 | 20 | :host([light-theme]) > small { 21 | color: color-mix(in oklab, hsl(228 calc(1 * 5.2%) 38% / 1) 100%, black 0%); 22 | } 23 | `; 24 | 25 | @consume({ context: messagesLightTheme }) 26 | @property({ type: Boolean, reflect: true, attribute: 'light-theme' }) 27 | public accessor lightTheme = false; 28 | 29 | protected override render() { 30 | return html` 31 | 32 | 33 | 34 | 35 | 36 | `; 37 | } 38 | } 39 | 40 | declare global { 41 | interface HTMLElementTagNameMap { 42 | 'discord-subscript': DiscordSubscript; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/core/src/components/discord-thread/DiscordThread.ts: -------------------------------------------------------------------------------- 1 | import { consume } from '@lit/context'; 2 | import { css, html, LitElement } from 'lit'; 3 | import { customElement, property } from 'lit/decorators.js'; 4 | import type { LightTheme } from '../../types.js'; 5 | import { messagesLightTheme } from '../discord-messages/DiscordMessages.js'; 6 | 7 | @customElement('discord-thread') 8 | export class DiscordThread extends LitElement implements LightTheme { 9 | /** 10 | * @internal 11 | */ 12 | public static override readonly styles = css` 13 | :host { 14 | background-color: #2f3136; 15 | border-radius: 4px; 16 | cursor: pointer; 17 | margin-top: 8px; 18 | max-width: 480px; 19 | min-width: 0; 20 | padding: 8px; 21 | display: inline-flex; 22 | width: fit-content; 23 | flex-direction: column; 24 | } 25 | 26 | :host([light-theme]) { 27 | background-color: #f2f3f5; 28 | } 29 | 30 | :host .discord-thread-top { 31 | display: flex; 32 | } 33 | 34 | :host .discord-thread-bottom { 35 | font-size: 0.875rem; 36 | line-height: 1.125rem; 37 | align-items: center; 38 | color: #b9bbbe; 39 | display: flex; 40 | margin-top: 2px; 41 | white-space: nowrap; 42 | } 43 | 44 | :host([light-theme]) .discord-thread-bottom { 45 | color: #4f5660; 46 | } 47 | 48 | :host .discord-thread-name { 49 | font-size: 0.875rem; 50 | font-weight: 600; 51 | line-height: 1.125rem; 52 | color: white; 53 | margin-right: 8px; 54 | overflow: hidden; 55 | text-overflow: ellipsis; 56 | white-space: nowrap; 57 | } 58 | 59 | :host([light-theme]) .discord-thread-name { 60 | color: #060607; 61 | } 62 | 63 | :host .discord-thread-cta { 64 | color: #00aff4; 65 | flex-shrink: 0; 66 | font-size: 0.875rem; 67 | font-weight: 600; 68 | line-height: 1.125rem; 69 | } 70 | 71 | :host .discord-thread-cta:hover { 72 | text-decoration: underline; 73 | } 74 | 75 | .discord-thread:hover .discord-thread-cta { 76 | text-decoration: underline; 77 | } 78 | `; 79 | 80 | /** 81 | * The name of the thread. 82 | */ 83 | @property() 84 | public accessor name = 'Thread'; 85 | 86 | /** 87 | * The the text within the call to action text. (i.e. 'See Thread' or 'x Messages') 88 | */ 89 | @property() 90 | public accessor cta = 'See Thread'; 91 | 92 | @consume({ context: messagesLightTheme }) 93 | @property({ type: Boolean, reflect: true, attribute: 'light-theme' }) 94 | public accessor lightTheme = false; 95 | 96 | protected override render() { 97 | return html` 98 |
    99 | ${this.name} 100 | 101 |
    102 | 103 | 104 | 105 | `; 106 | } 107 | } 108 | 109 | declare global { 110 | interface HTMLElementTagNameMap { 111 | 'discord-thread': DiscordThread; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /packages/core/src/components/discord-time/DiscordTime.ts: -------------------------------------------------------------------------------- 1 | import { css, html, LitElement } from 'lit'; 2 | import { customElement } from 'lit/decorators.js'; 3 | 4 | @customElement('discord-time') 5 | export class DiscordTime extends LitElement { 6 | /** 7 | * @internal 8 | */ 9 | public static override readonly styles = css` 10 | :host { 11 | white-space: nowrap; 12 | background-color: #ffffff0f; 13 | border-radius: 3px; 14 | padding: 0 2px; 15 | } 16 | `; 17 | 18 | protected override render() { 19 | return html``; 20 | } 21 | } 22 | 23 | declare global { 24 | interface HTMLElementTagNameMap { 25 | 'discord-time': DiscordTime; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/core/src/components/discord-underlined/DiscordUnderlined.ts: -------------------------------------------------------------------------------- 1 | import { html, LitElement } from 'lit'; 2 | import { customElement } from 'lit/decorators.js'; 3 | 4 | @customElement('discord-underlined') 5 | export class DiscordUnderlined extends LitElement { 6 | protected override render() { 7 | return html` 8 | 9 | 10 | 11 | `; 12 | } 13 | } 14 | 15 | declare global { 16 | interface HTMLElementTagNameMap { 17 | 'discord-underlined': DiscordUnderlined; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/core/src/components/discord-unordered-list/DiscordUnorderedList.ts: -------------------------------------------------------------------------------- 1 | import { css, html, LitElement } from 'lit'; 2 | import { customElement, property } from 'lit/decorators.js'; 3 | import { DiscordComponentsError } from '../../util.js'; 4 | 5 | @customElement('discord-unordered-list') 6 | export class DiscordUnorderedList extends LitElement { 7 | /** 8 | * @internal 9 | */ 10 | public static override readonly styles = css` 11 | :host > ul { 12 | list-style-image: initial; 13 | list-style: disc; 14 | list-style-position: outside; 15 | margin: 4px 0 0 16px; 16 | padding: 0px; 17 | } 18 | 19 | :host([nested]) > ul { 20 | list-style: circle; 21 | } 22 | `; 23 | 24 | /** 25 | * Whether this is a nested list or not, this will change the style of the list-style. 26 | * 27 | * The library will try to guess this automatically based on the component tree, but 28 | * you can also set this manually. 29 | * 30 | * @defaultValue false 31 | */ 32 | @property({ type: Boolean, reflect: true }) 33 | public accessor nested = false; 34 | 35 | public checkChildren() { 36 | const allChildrenAreListItems = Array.from(this.children).every((child) => { 37 | const tagNameLowerCase = child.tagName.toLowerCase(); 38 | return ( 39 | tagNameLowerCase === 'discord-list-item' || 40 | tagNameLowerCase === 'discord-unordered-list' || 41 | tagNameLowerCase === 'discord-ordered-list' 42 | ); 43 | }); 44 | 45 | if (!allChildrenAreListItems) { 46 | throw new DiscordComponentsError( 47 | 'All direct children inside of a components must be one of , , or .' 48 | ); 49 | } 50 | } 51 | 52 | /** 53 | * Sets {@link DiscordUnorderedList.nested | nested} to true if the parent is either 54 | * `` or ``. 55 | */ 56 | protected override willUpdate(): void { 57 | if ( 58 | this.parentElement?.tagName.toLowerCase() === 'discord-unordered-list' || 59 | this.parentElement?.tagName.toLowerCase() === 'discord-ordered-list' 60 | ) { 61 | this.nested = true; 62 | } 63 | } 64 | 65 | protected override render() { 66 | this.checkChildren(); 67 | 68 | // We disable the eslint rule here because users should use the component inside of this component. 69 | // eslint-disable-next-line lit-a11y/list 70 | return html`
      71 | 72 |
    `; 73 | } 74 | } 75 | 76 | declare global { 77 | interface HTMLElementTagNameMap { 78 | 'discord-unordered-list': DiscordUnorderedList; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /packages/core/src/components/discord-verified-author-tag/DiscordVerifiedAuthorTag.ts: -------------------------------------------------------------------------------- 1 | import { consume } from '@lit/context'; 2 | import { css, html, LitElement } from 'lit'; 3 | import { customElement, property } from 'lit/decorators.js'; 4 | import { when } from 'lit/directives/when.js'; 5 | import { messagesCompactMode } from '../discord-messages/DiscordMessages.js'; 6 | import VerifiedTick from '../svgs/VerifiedTick.js'; 7 | 8 | @customElement('discord-verified-author-tag') 9 | export class DiscordVerifiedAuthorTag extends LitElement { 10 | public static override readonly styles = css` 11 | :host { 12 | background-color: #5865f2; 13 | color: #fff; 14 | font-size: 0.625em; 15 | margin-left: 4px; 16 | border-radius: 3px; 17 | line-height: 100%; 18 | text-transform: uppercase; 19 | display: inline-flex; 20 | align-items: center; 21 | height: 0.9375rem; 22 | padding: 0 0.275rem; 23 | margin-top: 0.075em; 24 | border-radius: 0.1875rem; 25 | } 26 | 27 | :host .discord-application-tag-verified { 28 | display: inline-block; 29 | width: 0.9375rem; 30 | height: 0.9375rem; 31 | margin-left: -0.25rem; 32 | } 33 | 34 | :host([compact-mode]) { 35 | padding-left: 10px; 36 | padding-right: 4px; 37 | margin-right: 0.25rem; 38 | margin-left: 0px !important; 39 | margin-top: 0px !important; 40 | } 41 | 42 | :host([compact-mode]) .discord-application-tag-verified { 43 | margin-right: 0.7em; 44 | margin-left: -0.7em; 45 | } 46 | `; 47 | 48 | /** 49 | * Whether this bot is verified by Discord. Only works if `bot` is `true` 50 | */ 51 | @property({ type: Boolean }) 52 | public accessor verified = false; 53 | 54 | /** 55 | * Whether to reverse the order of the author info for compact mode. 56 | */ 57 | @consume({ context: messagesCompactMode }) 58 | @property({ type: Boolean, reflect: true, attribute: 'compact-mode' }) 59 | public accessor compactMode = false; 60 | 61 | protected override render() { 62 | return html`${when(this.verified, () => VerifiedTick())}App`; 63 | } 64 | } 65 | 66 | declare global { 67 | interface HTMLElementTagNameMap { 68 | 'discord-verified-author-tag': DiscordVerifiedAuthorTag; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/AttachmentDownloadButton.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 9 | `; 10 | 11 | export default function AttachmentDownloadButton(props: Record = {}) { 12 | return html``; 25 | } 26 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/AttachmentReply.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 9 | `; 10 | 11 | export default function AttachmentReply(props: Record = {}) { 12 | return html`${svgContent}`; 13 | } 14 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/Boost.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | 7 | `; 8 | 9 | export default function Boost(props: Record = {}) { 10 | return html`${svgContent}`; 11 | } 12 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/ChannelForum.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 9 | 13 | `; 14 | 15 | export default function ChannelForum(props: Record = {}) { 16 | return html` 25 | ${svgContent} 26 | `; 27 | } 28 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/ChannelIcon.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 11 | `; 12 | 13 | export default function ChannelIcon(props: Record = {}) { 14 | return html` 23 | ${svgContent} 24 | `; 25 | } 26 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/ChannelThread.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 9 | `; 10 | 11 | export default function ChannelThread(props: Record = {}) { 12 | return html` 21 | ${svgContent} 22 | `; 23 | } 24 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/ChannelsAndRoles.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 11 | 15 | `; 16 | 17 | export default function ChannelsAndRoles(props: Record = {}) { 18 | return html` 29 | ${svgContent} 30 | `; 31 | } 32 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/CommandIcon.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | `; 7 | 8 | export default function CommandIcon(props: Record = {}) { 9 | return html` 10 | ${svgContent} 11 | `; 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/CommandIconName.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | 7 | `; 8 | 9 | export default function CommandIconName(props: Record = {}) { 10 | return html` 11 | ${svgContent} 12 | `; 13 | } 14 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/CommandReply.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 11 | `; 12 | 13 | export default function CommandReply(props: Record) { 14 | return html` 15 | ${svgContent} 16 | `; 17 | } 18 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/CustomizeCommunity.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 9 | `; 10 | 11 | export default function CustomizeCommunity(props: Record = {}) { 12 | return html` 23 | ${svgContent} 24 | `; 25 | } 26 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/DMCall.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 10 | `; 11 | 12 | export default function DMCall(props: Record = {}) { 13 | return html`${svgContent}`; 14 | } 15 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/DMEdit.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | 10 | 11 | 12 | `; 13 | 14 | export default function DMEdit(props: Record = {}) { 15 | return html`${svgContent}`; 16 | } 17 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/DMMissedCall.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | 7 | 11 | 12 | `; 13 | 14 | export default function DMMissedCall(props: Record = {}) { 15 | return html`${svgContent}`; 16 | } 17 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/Ephemeral.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 9 | 15 | `; 16 | 17 | export default function Ephemeral(props: Record = {}) { 18 | return html` 27 | ${svgContent} 28 | `; 29 | } 30 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/ExpandMore.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | `; 7 | 8 | export default function ExpandMore(props: Record = {}) { 9 | return html` 10 | ${svgContent} 11 | `; 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/FileAttachment.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 8 | `; 9 | 10 | export default function FileAttachment(props: Record = {}) { 11 | return html`${svgContent}`; 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/GuildBadge.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 10 | `; 11 | 12 | export default function GuildBadge(props: Record) { 13 | return html` 14 | ${svgContent} 15 | `; 16 | } 17 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/LaunchIcon.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 9 | 11 | `; 12 | 13 | export default function LaunchIcon(props: Record = {}) { 14 | return html` 15 | ${svgContent} 16 | `; 17 | } 18 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/LockedVoiceChannel.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 11 | `; 12 | 13 | export default function LockedVoiceChannel(props: Record = {}) { 14 | return html` 23 | ${svgContent} 24 | `; 25 | } 26 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/MediaPauseIcon.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | `; 7 | 8 | export default function AudioVideoPauseIcon(props: Record = {}) { 9 | return html``; 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/MediaPlayIcon.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | `; 7 | 8 | export default function AudioVideoPlayIcon(props: Record = {}) { 9 | return html``; 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/MediaRestartIcon.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | `; 7 | 8 | export default function AudioVideoRestartIcon(props: Record = {}) { 9 | return html``; 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/MediaVolumeAbove50PercentIcon.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | 7 | `; 8 | 9 | export default function AudioVideoVolumeAbove50PercentIcon(props: Record = {}) { 10 | return html``; 11 | } 12 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/MediaVolumeBelow50PercentIcon.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | `; 7 | 8 | export default function AudioVideoVolumeBelow50PercentIcon(props: Record = {}) { 9 | return html``; 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/MediaVolumeMutedIcon.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | `; 7 | 8 | export default function AudioVideoVolumeMutedIcon(props: Record = {}) { 9 | return html``; 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/MessageIcon.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | 7 | `; 8 | 9 | export default function CommandIconName(props: Record = {}) { 10 | return html`${svgContent}`; 11 | } 12 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/ModalClose.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | `; 7 | 8 | export default function ModalClose(props: Record = {}) { 9 | return html` 10 | ${svgContent} 11 | `; 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/ModalWarning.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | 7 | `; 8 | 9 | export default function ModalWarning(props: Record = {}) { 10 | return html` 11 | ${svgContent} 12 | `; 13 | } 14 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/PartnerBadgeOverlay.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 9 | 13 | `; 14 | 15 | export default function PartnerBadgeOverlay(props: Record = {}) { 16 | return html` 17 | ${svgContent} 18 | `; 19 | } 20 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/Pin.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 9 | `; 10 | 11 | export default function Thread(props: Record = {}) { 12 | return html`${svgContent}`; 13 | } 14 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/ReplyIcon.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 9 | `; 10 | 11 | export default function ReplyIcon(props: Record = {}) { 12 | return html`${svgContent}`; 13 | } 14 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/ServerGuide.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 9 | `; 10 | 11 | export default function ServerGuide(props: Record = {}) { 12 | return html` 23 | ${svgContent} 24 | `; 25 | } 26 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/ServerUpgrade.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 11 | `; 12 | 13 | export default function ServerUpgrade(props: Record = {}) { 14 | return html`${svgContent}`; 15 | } 16 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/SystemAlert.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 11 | `; 12 | 13 | export default function SystemAlert(props: Record = {}) { 14 | return html`${svgContent}`; 15 | } 16 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/SystemError.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 10 | `; 11 | 12 | export default function SystemError(props: Record = {}) { 13 | return html`${svgContent}`; 14 | } 15 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/Thread.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 9 | 13 | `; 14 | 15 | export default function Thread(props: Record = {}) { 16 | return html`${svgContent}`; 17 | } 18 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/UserJoin.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | 7 | 8 | 9 | `; 10 | 11 | export default function UserJoin(props: Record = {}) { 12 | return html`${svgContent}`; 13 | } 14 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/UserLeave.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | 7 | 8 | 9 | `; 10 | 11 | export default function UserLeave(props: Record = {}) { 12 | return html`${svgContent}`; 13 | } 14 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/VerifiedBadgeOverlay.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | `; 7 | 8 | export default function VerifiedBadgeOverlay(props: Record = {}) { 9 | return html` 10 | ${svgContent} 11 | `; 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/VerifiedTick.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | `; 7 | 8 | export default function VerifiedTick(props: Record = {}) { 9 | return html` 19 | ${svgContent} 20 | `; 21 | } 22 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/VideoFullScreenIcon.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | `; 7 | 8 | export default function VideoFullScreenIcon(props: Record = {}) { 9 | return html``; 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/VideoPausePopIcon.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | `; 7 | 8 | export default function VideoPausePopIcon(props: Record = {}) { 9 | return html``; 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/VideoPlayPopIcon.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | `; 7 | 8 | export default function VideoPlayPopIcon(props: Record = {}) { 9 | return html``; 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/VoiceChannel.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 9 | 13 | `; 14 | 15 | export default function VoiceChannel(props: Record = {}) { 16 | return html` 25 | ${svgContent} 26 | `; 27 | } 28 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/clan-icons/Crystal.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | `; 21 | 22 | export default function Crystal(props: Record = {}) { 23 | return html``; 35 | } 36 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/clan-icons/Explosion.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | `; 24 | 25 | export default function Explosion(props: Record = {}) { 26 | return html``; 38 | } 39 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/clan-icons/Flame.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | `; 17 | 18 | export default function Flame(props: Record = {}) { 19 | return html``; 31 | } 32 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/clan-icons/Flower.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | 7 | 8 | 9 | 10 | 11 | `; 12 | 13 | export default function Flower(props: Record = {}) { 14 | return html``; 26 | } 27 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/clan-icons/Heart.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | `; 13 | 14 | export default function Heart(props: Record = {}) { 15 | return html``; 27 | } 28 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/clan-icons/Key.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | `; 26 | 27 | export default function Key(props: Record = {}) { 28 | return html``; 40 | } 41 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/clan-icons/Leaf.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | `; 17 | 18 | export default function Leaf(props: Record = {}) { 19 | return html``; 31 | } 32 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/clan-icons/Lightning.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | `; 19 | 20 | export default function Lightning(props: Record = {}) { 21 | return html``; 33 | } 34 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/clan-icons/Magic.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../../spread.js'; 3 | 4 | const svgContent = svg` 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 | export default function Magic(props: Record = {}) { 30 | return html``; 42 | } 43 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/clan-icons/Moon.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | `; 15 | 16 | export default function Moon(props: Record = {}) { 17 | return html``; 29 | } 30 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/clan-icons/Mushroom.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | `; 27 | 28 | export default function Mushroom(props: Record = {}) { 29 | return html``; 41 | } 42 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/clan-icons/Plasma.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | `; 17 | 18 | export default function Plasma(props: Record = {}) { 19 | return html``; 31 | } 32 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/clan-icons/Rock.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | `; 14 | 15 | export default function Rock(props: Record = {}) { 16 | return html``; 28 | } 29 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/clan-icons/Shell.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | `; 23 | 24 | export default function Shell(props: Record = {}) { 25 | return html``; 37 | } 38 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/clan-icons/Skull.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | `; 22 | 23 | export default function Skull(props: Record = {}) { 24 | return html``; 36 | } 37 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/clan-icons/Sword.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | `; 21 | 22 | export default function Sword(props: Record = {}) { 23 | return html``; 35 | } 36 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/clan-icons/Water.ts: -------------------------------------------------------------------------------- 1 | import { html, svg } from 'lit'; 2 | import { spread } from '../../../spread.js'; 3 | 4 | const svgContent = svg` 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | `; 14 | 15 | export default function Water(props: Record = {}) { 16 | return html``; 28 | } 29 | -------------------------------------------------------------------------------- /packages/core/src/components/svgs/clan-icons/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Crystal } from './Crystal.js'; 2 | export { default as Diamond } from './Diamond.js'; 3 | export { default as Explosion } from './Explosion.js'; 4 | export { default as Flame } from './Flame.js'; 5 | export { default as Flower } from './Flower.js'; 6 | export { default as Heart } from './Heart.js'; 7 | export { default as Key } from './Key.js'; 8 | export { default as Leaf } from './Leaf.js'; 9 | export { default as Lightning } from './Lightning.js'; 10 | export { default as Magic } from './Magic.js'; 11 | export { default as Moon } from './Moon.js'; 12 | export { default as Mushroom } from './Mushroom.js'; 13 | export { default as Mythical } from './Mythical.js'; 14 | export { default as Ornament } from './Ornament.js'; 15 | export { default as Plasma } from './Plasma.js'; 16 | export { default as Rock } from './Rock.js'; 17 | export { default as Shell } from './Shell.js'; 18 | export { default as Skull } from './Skull.js'; 19 | export { default as Sun } from './Sun.js'; 20 | export { default as Sword } from './Sword.js'; 21 | export { default as Water } from './Water.js'; 22 | -------------------------------------------------------------------------------- /packages/core/src/config.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Crystal, 3 | Diamond, 4 | Explosion, 5 | Flame, 6 | Flower, 7 | Heart, 8 | Key, 9 | Leaf, 10 | Lightning, 11 | Magic, 12 | Moon, 13 | Mushroom, 14 | Mythical, 15 | Ornament, 16 | Plasma, 17 | Rock, 18 | Shell, 19 | Skull, 20 | Sun, 21 | Sword, 22 | Water 23 | } from './components/svgs/clan-icons/index.js'; 24 | import type { Avatars, DiscordMessageOptions, Profile } from './types.js'; 25 | 26 | let config: DiscordMessageOptions = globalThis.$discordMessage ?? {}; 27 | 28 | export function getConfig(): DiscordMessageOptions { 29 | return config; 30 | } 31 | 32 | export function setConfig(partialConfig: Partial): void { 33 | config = Object.assign(config, partialConfig); 34 | } 35 | 36 | export const defaultDiscordAvatars: Omit = { 37 | blue: 'https://cdn.discordapp.com/embed/avatars/0.png', 38 | gray: 'https://cdn.discordapp.com/embed/avatars/1.png', 39 | green: 'https://cdn.discordapp.com/embed/avatars/2.png', 40 | orange: 'https://cdn.discordapp.com/embed/avatars/3.png', 41 | red: 'https://cdn.discordapp.com/embed/avatars/4.png', 42 | pink: 'https://cdn.discordapp.com/embed/avatars/5.png' 43 | }; 44 | 45 | const globalAvatars: Avatars = getConfig().avatars ?? ({} as Avatars); 46 | 47 | export const avatars: Avatars = Object.assign(defaultDiscordAvatars, globalAvatars, { 48 | default: defaultDiscordAvatars[globalAvatars.default] ?? globalAvatars.default ?? defaultDiscordAvatars.blue 49 | }); 50 | 51 | export const profiles: { [key: string]: Profile } = getConfig().profiles ?? {}; 52 | 53 | export const defaultTheme: string = getConfig().defaultTheme === 'light' ? 'light' : 'dark'; 54 | 55 | export const defaultMode: string = getConfig().defaultMode === 'compact' ? 'compact' : 'cozy'; 56 | 57 | export const defaultBackground: string = getConfig().defaultBackground === 'none' ? 'none' : 'discord'; 58 | 59 | export const icons = new Map([ 60 | ['heart', Heart()], 61 | ['crystal', Crystal()], 62 | ['diamond', Diamond()], 63 | ['explosion', Explosion()], 64 | ['flame', Flame()], 65 | ['flower', Flower()], 66 | ['key', Key()], 67 | ['leaf', Leaf()], 68 | ['lightning', Lightning()], 69 | ['magic', Magic()], 70 | ['moon', Moon()], 71 | ['mushroom', Mushroom()], 72 | ['mythical', Mythical()], 73 | ['ornament', Ornament()], 74 | ['plasma', Plasma()], 75 | ['rock', Rock()], 76 | ['shell', Shell()], 77 | ['skull', Skull()], 78 | ['sun', Sun()], 79 | ['sword', Sword()], 80 | ['water', Water()] 81 | ]); 82 | -------------------------------------------------------------------------------- /packages/core/src/hex-to-rgba.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable id-length */ 2 | 3 | function removeHash(color: string) { 4 | return color.startsWith('#') ? color.slice(1) : color; 5 | } 6 | 7 | function parseHex(nakedHex: string): HexObject { 8 | const isShort = nakedHex.length === 3 || nakedHex.length === 4; 9 | 10 | const twoDigitHexR = isShort ? `${nakedHex.slice(0, 1)}${nakedHex.slice(0, 1)}` : nakedHex.slice(0, 2); 11 | const twoDigitHexG = isShort ? `${nakedHex.slice(1, 2)}${nakedHex.slice(1, 2)}` : nakedHex.slice(2, 4); 12 | const twoDigitHexB = isShort ? `${nakedHex.slice(2, 3)}${nakedHex.slice(2, 3)}` : nakedHex.slice(4, 6); 13 | const twoDigitHexA = (isShort ? `${nakedHex.slice(3, 4)}${nakedHex.slice(3, 4)}` : nakedHex.slice(6, 8)) || 'ff'; 14 | 15 | return { 16 | r: twoDigitHexR, 17 | g: twoDigitHexG, 18 | b: twoDigitHexB, 19 | a: twoDigitHexA 20 | }; 21 | } 22 | 23 | function hexToDecimal(hex: string) { 24 | return Number.parseInt(hex, 16); 25 | } 26 | 27 | function hexesToDecimals({ r, g, b, a }: HexObject) { 28 | return { 29 | r: hexToDecimal(r), 30 | g: hexToDecimal(g), 31 | b: hexToDecimal(b), 32 | a: Number((hexToDecimal(a) / 255).toFixed(2)) 33 | }; 34 | } 35 | 36 | function isNumeric(n: number | string | undefined) { 37 | return typeof n === 'number' || (typeof n === 'string' && Number.isFinite(Number(n))); 38 | } 39 | 40 | function formatRgb(decimalObject: DecimalObject, parameterA?: number | string) { 41 | const { r, g, b, a: parsedA } = decimalObject; 42 | const a = isNumeric(parameterA) ? parameterA : parsedA; 43 | 44 | return `rgba(${r}, ${g}, ${b}, ${a})`; 45 | } 46 | 47 | /** 48 | * Turns an old-fashioned css hex color value into a rgb color value. 49 | * 50 | * If you specify an alpha value, you'll get a rgba() value instead. 51 | * 52 | * @param color - The hex value to convert. ('123456'. '#123456', ''123', '#123') 53 | * @param alpha - An alpha value to apply. (optional) ('0.5', '0.25') 54 | * @returns An rgb or rgba value. ('rgb(11, 22, 33)'. 'rgba(11, 22, 33, 0.5)') 55 | */ 56 | export function hexToRgba(color: string, alpha?: number | string) { 57 | const hashlessHex = removeHash(color); 58 | const hexObject = parseHex(hashlessHex); 59 | const decimalObject = hexesToDecimals(hexObject); 60 | 61 | return formatRgb(decimalObject, alpha); 62 | } 63 | 64 | interface HexObject { 65 | a: string; 66 | b: string; 67 | g: string; 68 | r: string; 69 | } 70 | 71 | interface DecimalObject { 72 | a: number; 73 | b: number; 74 | g: number; 75 | r: number; 76 | } 77 | -------------------------------------------------------------------------------- /packages/core/src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "../dist", 6 | "skipLibCheck": true, 7 | "plugins": [ 8 | { 9 | "name": "ts-lit-plugin" 10 | } 11 | ] 12 | }, 13 | "include": ["."] 14 | } 15 | -------------------------------------------------------------------------------- /packages/core/src/types.ts: -------------------------------------------------------------------------------- 1 | export type DiscordTimestamp = Date | string | null; 2 | 3 | export interface LightTheme { 4 | lightTheme: boolean; 5 | } 6 | 7 | export interface Avatars { 8 | [key: string]: string | undefined; 9 | blue?: string; 10 | default: 'blue' | 'gray' | 'green' | 'orange' | 'red'; 11 | gray?: string; 12 | green?: string; 13 | orange?: string; 14 | red?: string; 15 | } 16 | 17 | export interface Profile { 18 | author?: string; 19 | avatar?: string; 20 | bot?: boolean; 21 | clanIcon?: string; 22 | clanTag?: string; 23 | officialApp?: boolean; 24 | op?: boolean; 25 | roleColor?: string; 26 | roleIcon?: string; 27 | roleName?: string; 28 | server?: boolean; 29 | verified?: boolean; 30 | } 31 | 32 | export interface DiscordMessageOptions { 33 | avatars?: Avatars; 34 | defaultBackground?: 'discord' | 'none'; 35 | defaultMode?: string; 36 | defaultTheme?: string; 37 | emojis?: { [key: string]: Emoji }; 38 | profiles?: { [key: string]: Profile }; 39 | } 40 | 41 | export interface Emoji { 42 | embedEmoji?: boolean; 43 | name?: string; 44 | url?: string; 45 | } 46 | -------------------------------------------------------------------------------- /packages/core/src/util.ts: -------------------------------------------------------------------------------- 1 | import { getConfig, icons } from './config.js'; 2 | import type { Emoji, DiscordTimestamp } from './types.js'; 3 | 4 | export class DiscordComponentsError extends Error { 5 | public constructor(message: string) { 6 | super(message); 7 | this.name = 'DiscordComponentsError'; 8 | } 9 | } 10 | 11 | const intlDateFormat = new Intl.DateTimeFormat('en-US', { day: '2-digit', month: '2-digit', year: 'numeric' }); 12 | const intlTwelveHourFormat = new Intl.DateTimeFormat('en-US', { hour12: true, hour: '2-digit', minute: '2-digit' }); 13 | const intlTwentyFourHourFormat = new Intl.DateTimeFormat('en-US', { hour12: false, hour: '2-digit', minute: '2-digit' }); 14 | 15 | const formatDate = (value: Exclude): string | null => { 16 | if (!(value instanceof Date)) return value; 17 | return intlDateFormat.format(value); 18 | }; 19 | 20 | const formatTime = (value: Exclude, hour24 = false): string | null => { 21 | if (!(value instanceof Date)) return value; 22 | if (hour24) return intlTwentyFourHourFormat.format(value); 23 | return intlTwelveHourFormat.format(value); 24 | }; 25 | 26 | export function handleTimestamp(value: DiscordTimestamp, useTime = false, hour24 = false): string | null { 27 | if (!(value instanceof Date) && typeof value !== 'string') { 28 | throw new TypeError('Timestamp prop must be a Date object or a string.'); 29 | } 30 | 31 | return useTime ? formatTime(value, hour24) : formatDate(value); 32 | } 33 | 34 | export const IMAGE_EXTENSION = /\.(?bmp|jpe?g|png|gif|webp|tiff)$/i; 35 | 36 | export function validateImageExtension(url: string) { 37 | if (!IMAGE_EXTENSION.test(url)) 38 | throw new DiscordComponentsError(`The url of an image for discord-image-attachment should match the regex ${IMAGE_EXTENSION}`); 39 | } 40 | 41 | const emojiRegex = /(?:<(?a)?:(?\w{2,32}):)?(?\d{17,21})>?/; 42 | export function getGlobalEmojiUrl(emojiName: string): Emoji | undefined { 43 | const globalEmoji = getConfig().emojis?.[emojiName]; 44 | if (globalEmoji) return globalEmoji; 45 | 46 | const match = emojiRegex.exec(emojiName); 47 | 48 | if (match?.groups) { 49 | const { name, id, animated } = match.groups; 50 | const extension = animated ? 'gif' : 'png'; 51 | 52 | return { 53 | name, 54 | url: `https://cdn.discordapp.com/emojis/${id}.${extension}` 55 | }; 56 | } 57 | 58 | return undefined; 59 | } 60 | 61 | /** 62 | * Get the image for a clan icon 63 | * 64 | * @param clanIcon - The clan icon to get the image for 65 | * @returns The image for the clan icon, or the clan icon itself if it's not found 66 | */ 67 | export function getClanIcon(clanIcon: string | undefined): object | string | undefined { 68 | if (!clanIcon) return undefined; 69 | 70 | return icons.get(clanIcon) ?? clanIcon; 71 | } 72 | -------------------------------------------------------------------------------- /packages/core/tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "include": ["src", "test"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/core/web-dev-server.config.mjs: -------------------------------------------------------------------------------- 1 | import { fileURLToPath, URL } from 'node:url'; 2 | import { esbuildPlugin } from '@web/dev-server-esbuild'; 3 | 4 | export default /** @type {import('@web/dev-server').DevServerConfig} */ ({ 5 | plugins: [ 6 | esbuildPlugin({ 7 | ts: true, 8 | target: 'es2020', 9 | tsconfig: fileURLToPath(new URL('src/tsconfig.json', import.meta.url)) 10 | }) 11 | ], 12 | 13 | open: '/demo/', 14 | watch: true, 15 | 16 | nodeResolve: { 17 | exportConditions: ['browser', 'development'] 18 | }, 19 | 20 | appIndex: 'demo/index.html' 21 | }); 22 | -------------------------------------------------------------------------------- /packages/documentation/.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | 4 | # generated types 5 | .astro/ 6 | 7 | # dependencies 8 | node_modules/ 9 | 10 | # Autogenerated documentation 11 | src/content/docs/api/ 12 | -------------------------------------------------------------------------------- /packages/documentation/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "documentation", 3 | "type": "module", 4 | "version": "4.0.0", 5 | "private": true, 6 | "scripts": { 7 | "dev": "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/lit": "^4.3.0", 15 | "@astrojs/starlight": "0.34.5", 16 | "@sapphire/docusaurus-plugin-npm2yarn2pnpm": "2.0.2", 17 | "@webcomponents/template-shadowroot": "^0.2.1", 18 | "astro": "5.11.0", 19 | "lit": "^3.3.1", 20 | "react": "^19.1.0", 21 | "starlight-typedoc": "0.21.3", 22 | "typedoc": "0.28.5", 23 | "typedoc-plugin-frontmatter": "^1.3.0", 24 | "typedoc-plugin-markdown": "4.6.4" 25 | }, 26 | "devDependencies": { 27 | "@skyra/discord-components-core": "workspace:^", 28 | "@skyra/discord-components-react": "workspace:^", 29 | "@types/react": "19.1.8", 30 | "@types/react-dom": "^19.1.6", 31 | "react-dom": "^19.1.0", 32 | "typescript": "^5.8.3" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/documentation/public/.nojekyll: -------------------------------------------------------------------------------- 1 | This file prevents GitHub Pages from using Jekyll. 2 | -------------------------------------------------------------------------------- /packages/documentation/public/CNAME: -------------------------------------------------------------------------------- 1 | discord-components.js.org 2 | -------------------------------------------------------------------------------- /packages/documentation/public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 -------------------------------------------------------------------------------- /packages/documentation/public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | #ffffff 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/documentation/public/favicons/android-chrome-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/android-chrome-144x144.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/android-chrome-192x192.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/android-chrome-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/android-chrome-256x256.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/android-chrome-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/android-chrome-36x36.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/android-chrome-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/android-chrome-384x384.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/android-chrome-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/android-chrome-48x48.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/android-chrome-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/android-chrome-72x72.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/android-chrome-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/android-chrome-96x96.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/apple-startup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/apple-startup.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/apple-touch-icon-114x114-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/apple-touch-icon-114x114-precomposed.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/apple-touch-icon-120x120-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/apple-touch-icon-120x120-precomposed.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/apple-touch-icon-144x144-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/apple-touch-icon-144x144-precomposed.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/apple-touch-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/apple-touch-icon-144x144.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/apple-touch-icon-152x152-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/apple-touch-icon-152x152-precomposed.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/apple-touch-icon-180x180-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/apple-touch-icon-180x180-precomposed.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/apple-touch-icon-57x57-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/apple-touch-icon-57x57-precomposed.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/apple-touch-icon-60x60-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/apple-touch-icon-60x60-precomposed.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/apple-touch-icon-72x72-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/apple-touch-icon-72x72-precomposed.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/apple-touch-icon-76x76-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/apple-touch-icon-76x76-precomposed.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/apple-touch-icon.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/favicon-16x16.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/favicon-32x32.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/favicon.ico -------------------------------------------------------------------------------- /packages/documentation/public/favicons/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/mstile-144x144.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/mstile-150x150.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/mstile-310x150.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/mstile-310x310.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/mstile-70x70.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/opengraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/favicons/opengraph.png -------------------------------------------------------------------------------- /packages/documentation/public/favicons/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | Created by potrace 1.14, written by Peter Selinger 2001-2017 12 | 13 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /packages/documentation/public/manifest.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Discord Components", 3 | "short_name": "Discord Components", 4 | "description": "Web components to easily build and display fake Discord messages on your webpages", 5 | "theme_color": "#5865F2", 6 | "background_color": "#131516", 7 | "display": "minimal-ui", 8 | "categories": ["discord", "library", "webcomponents", "lit", "components", "html", "documentation"], 9 | "dir": "ltr", 10 | "lang": "en", 11 | "orientation": "portrait-primary", 12 | "scope": "/", 13 | "start_url": "/", 14 | "icons": [ 15 | { 16 | "src": "https://discord-components.js.org/favicons/android-chrome-36x36.png", 17 | "sizes": "36x36", 18 | "type": "image/png" 19 | }, 20 | { 21 | "src": "https://discord-components.js.org/favicons/android-chrome-48x48.png", 22 | "sizes": "48x48", 23 | "type": "image/png" 24 | }, 25 | { 26 | "src": "https://discord-components.js.org/favicons/android-chrome-72x72.png", 27 | "sizes": "72x72", 28 | "type": "image/png" 29 | }, 30 | { 31 | "src": "https://discord-components.js.org/favicons/android-chrome-96x96.png", 32 | "sizes": "96x96", 33 | "type": "image/png" 34 | }, 35 | { 36 | "src": "https://discord-components.js.org/favicons/android-chrome-144x144.png", 37 | "sizes": "144x144", 38 | "type": "image/png" 39 | }, 40 | { 41 | "src": "https://discord-components.js.org/favicons/android-chrome-192x192.png", 42 | "sizes": "192x192", 43 | "type": "image/png" 44 | }, 45 | { 46 | "src": "https://discord-components.js.org/favicons/android-chrome-256x256.png", 47 | "sizes": "256x256", 48 | "type": "image/png" 49 | }, 50 | { 51 | "src": "https://discord-components.js.org/favicons/android-chrome-384x384.png", 52 | "sizes": "384x384", 53 | "type": "image/png" 54 | } 55 | ], 56 | "shortcuts": [ 57 | { 58 | "name": "Discord Components", 59 | "short_name": "Discord Components", 60 | "description": "Web components to easily build and display fake Discord messages on your webpages", 61 | "url": "/", 62 | "icons": [ 63 | { 64 | "src": "https://discord-components.js.org/favicons/android-chrome-96x96.png", 65 | "sizes": "96x96", 66 | "type": "image/png" 67 | } 68 | ] 69 | } 70 | ], 71 | "screenshots": [ 72 | { 73 | "src": "/screenshots/pwa-desktop.png", 74 | "sizes": "1200x710", 75 | "type": "image/png", 76 | "form_factor": "wide", 77 | "label": "Discord Components" 78 | }, 79 | { 80 | "src": "/screenshots/pwa-mobile.png", 81 | "sizes": "480x640", 82 | "type": "image/png", 83 | "form_factor": "narrow", 84 | "label": "Discord Components" 85 | } 86 | ] 87 | } 88 | -------------------------------------------------------------------------------- /packages/documentation/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /packages/documentation/public/screenshots/pwa-desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/screenshots/pwa-desktop.png -------------------------------------------------------------------------------- /packages/documentation/public/screenshots/pwa-mobile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/public/screenshots/pwa-mobile.png -------------------------------------------------------------------------------- /packages/documentation/src/assets/discord-components-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyra-project/discord-components/cbb5c4230d0a19e713ac4fbb76e55d7dcb0ba558/packages/documentation/src/assets/discord-components-logo.png -------------------------------------------------------------------------------- /packages/documentation/src/assets/styles.css: -------------------------------------------------------------------------------- 1 | .action:where(.astro-button).primary { 2 | height: 3rem; 3 | background: var(--sl-color-text-accent); 4 | color: var(--sl-color-black); 5 | } 6 | 7 | @media (min-width: 50rem) { 8 | .action:where(.astro-button) { 9 | font-size: var(--sl-text-base); 10 | padding: 1rem 1.25rem; 11 | } 12 | } 13 | 14 | .action:where(.astro-button) { 15 | gap: 0.5em; 16 | align-items: center; 17 | border-radius: 999rem; 18 | padding: 0.5rem 1.125rem; 19 | color: var(--sl-color-black) !important; 20 | line-height: 1.1875; 21 | text-decoration: none; 22 | font-size: var(--sl-text-sm); 23 | } 24 | -------------------------------------------------------------------------------- /packages/documentation/src/components/astro/images/angular.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 20 | 24 | 25 | -------------------------------------------------------------------------------- /packages/documentation/src/components/astro/images/astro.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 10 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /packages/documentation/src/components/astro/images/htmx.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | 15 | 19 | 23 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /packages/documentation/src/components/astro/images/nextjs.svg: -------------------------------------------------------------------------------- 1 | 2 | 11 | Next.js 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /packages/documentation/src/components/astro/images/nuxt.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /packages/documentation/src/components/astro/images/preact.svg: -------------------------------------------------------------------------------- 1 | 26 | -------------------------------------------------------------------------------- /packages/documentation/src/components/astro/images/qwik.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/documentation/src/components/astro/images/solid.svg: -------------------------------------------------------------------------------- 1 | 61 | -------------------------------------------------------------------------------- /packages/documentation/src/components/astro/images/svelte.svg: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /packages/documentation/src/components/astro/images/vite.svg: -------------------------------------------------------------------------------- 1 | 32 | -------------------------------------------------------------------------------- /packages/documentation/src/components/astro/images/vue.svg: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /packages/documentation/src/content/config.ts: -------------------------------------------------------------------------------- 1 | import { docsSchema } from '@astrojs/starlight/schema'; 2 | import { defineCollection } from 'astro:content'; 3 | 4 | export const collections = { 5 | docs: defineCollection({ schema: docsSchema() }) 6 | }; 7 | -------------------------------------------------------------------------------- /packages/documentation/src/content/docs/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Discord Components 3 | description: Discord message components to easily build and display fake Discord messages on your webpage. 4 | template: splash 5 | hero: 6 | tagline: Discord message components to easily build and display fake Discord messages on your webpage. 7 | image: 8 | file: ../../assets/discord-components-logo.png 9 | actions: 10 | - text: Documentation 11 | link: /api/readme 12 | icon: right-arrow 13 | variant: primary 14 | --- 15 | 16 | import { Card, CardGrid, Code, Icon } from '@astrojs/starlight/components'; 17 | import { Tabs, TabItem } from '@astrojs/starlight/components'; 18 | import { npmToBun, npmToYarn, npmToPnpm } from '@sapphire/docusaurus-plugin-npm2yarn2pnpm'; 19 | import Templates from '../../components/astro/framework-grid.astro'; 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 | -------------------------------------------------------------------------------- /packages/documentation/src/content/docs/samples.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Samples 3 | description: This page shows various samples of Discord messages built using Discord Components. 4 | template: doc 5 | tableOfContents: false 6 | --- 7 | 8 | import { DiscordComponentsWrapper } from '../../components/lit/DiscordComponentsWrapper.ts'; 9 | 10 | 11 | -------------------------------------------------------------------------------- /packages/documentation/src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | -------------------------------------------------------------------------------- /packages/documentation/src/plugins/frontmatter.js: -------------------------------------------------------------------------------- 1 | import { MarkdownPageEvent } from 'typedoc-plugin-markdown'; 2 | 3 | /** 4 | * @param {import('typedoc-plugin-markdown').MarkdownApplication} app 5 | */ 6 | export function load(app) { 7 | app.renderer.on( 8 | MarkdownPageEvent.BEGIN, 9 | /** 10 | * @param {MarkdownPageEvent} page 11 | */ 12 | (page) => { 13 | if (page.url === 'README.md') { 14 | page.frontmatter.tableOfContents = { 15 | minHeadingLevel: 1, 16 | maxHeadingLevel: 4 17 | }; 18 | } 19 | } 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /packages/documentation/src/utils/seoConfig.ts: -------------------------------------------------------------------------------- 1 | const email = 'contact@skyra.pw'; 2 | const title = 'Discord Components'; 3 | 4 | /** 5 | * Defines the default SEO configuration for the website. 6 | */ 7 | export const seoConfig = { 8 | baseURL: 'https://discord-components.js.org/', 9 | description: 'Web components to easily build and display fake Discord messages on your webpages', 10 | email, 11 | type: 'website', 12 | image: { 13 | url: 'https://discord-components.js.org/favicons/opengraph.png', 14 | alt: 'OpenGraph image', 15 | width: 1_024, 16 | height: 512 17 | }, 18 | siteName: title, 19 | twitter: { 20 | card: 'summary_large_image', 21 | handle: '@favna_' 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /packages/documentation/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/strictest" 3 | } 4 | -------------------------------------------------------------------------------- /packages/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@skyra/discord-components-react", 3 | "version": "4.0.2", 4 | "description": "React bindings for @skyra/discord-components-core", 5 | "author": "@skyra", 6 | "license": "MIT", 7 | "main": "dist/cjs/index.cjs", 8 | "module": "dist/esm/index.mjs", 9 | "typings": "dist/cjs/index.d.cts", 10 | "type": "module", 11 | "exports": { 12 | "import": { 13 | "types": "./dist/esm/index.d.mts", 14 | "default": "./dist/esm/index.mjs" 15 | }, 16 | "require": { 17 | "types": "./dist/cjs/index.d.cts", 18 | "default": "./dist/cjs/index.cjs" 19 | } 20 | }, 21 | "sideEffects": [ 22 | "./dist/cjs/index.cjs", 23 | "./dist/esm/index.mjs" 24 | ], 25 | "homepage": "https://github.com/skyra-project/discord-components/tree/main/packages/react#readme", 26 | "scripts": { 27 | "build": "tsup && yarn build:rename-esm-index", 28 | "build:rename-esm-index": "node scripts/rename-esm-index.mjs", 29 | "prepack": "yarn build" 30 | }, 31 | "dependencies": { 32 | "@lit/react": "^1.0.8", 33 | "@skyra/discord-components-core": "workspace:^", 34 | "react": "^19.1.0" 35 | }, 36 | "devDependencies": { 37 | "@types/react": "^19.1.8", 38 | "colorette": "^2.0.20", 39 | "rimraf": "^6.0.1", 40 | "tsup": "^8.5.0", 41 | "typescript": "^5.8.3" 42 | }, 43 | "peerDependencies": { 44 | "react": "16.8.x || 17.x || 18.x || 19.x", 45 | "react-dom": "16.8.x || 17.x || 18.x || 19.x" 46 | }, 47 | "files": [ 48 | "dist/", 49 | "CHANGELOG.md" 50 | ], 51 | "engines": { 52 | "node": ">=v18" 53 | }, 54 | "publishConfig": { 55 | "access": "public" 56 | }, 57 | "repository": { 58 | "type": "git", 59 | "url": "git+https://github.com/skyra-project/discord-components.git" 60 | }, 61 | "bugs": { 62 | "url": "https://github.com/skyra-project/discord-components/issues" 63 | }, 64 | "keywords": [ 65 | "skyra", 66 | "typescript", 67 | "ts", 68 | "yarn", 69 | "discord", 70 | "bot", 71 | "components", 72 | "webcomponents", 73 | "lit", 74 | "react" 75 | ] 76 | } 77 | -------------------------------------------------------------------------------- /packages/react/scripts/rename-esm-index.mjs: -------------------------------------------------------------------------------- 1 | import { rename } from 'node:fs/promises'; 2 | import { join } from 'node:path'; 3 | import process from 'node:process'; 4 | import { green } from 'colorette'; 5 | 6 | const inputPath = 'dist/esm/index.d.ts'; 7 | const outputPath = 'dist/esm/index.d.mts'; 8 | 9 | const fullInputPathUrl = join(process.cwd(), inputPath); 10 | const fullOutputPathUrl = join(process.cwd(), outputPath); 11 | 12 | await rename(fullInputPathUrl, fullOutputPathUrl); 13 | 14 | console.log(green(`✅ Renamed index.d.ts to index.d.mts`)); 15 | -------------------------------------------------------------------------------- /packages/react/src/react-components/createComponent.ts: -------------------------------------------------------------------------------- 1 | import { createComponent } from '@lit/react'; 2 | import type { LitElement } from 'lit'; 3 | import React from 'react'; 4 | 5 | declare interface Constructor { 6 | new (): T; 7 | } 8 | 9 | export function createReactComponent(tagName: string, elementClass: Constructor) { 10 | return createComponent({ 11 | tagName, 12 | elementClass, 13 | react: React 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /packages/react/src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "../dist" 6 | }, 7 | "include": ["."] 8 | } 9 | -------------------------------------------------------------------------------- /packages/react/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, type Options } from 'tsup'; 2 | import { dependencies } from './package.json'; 3 | 4 | const baseOptions: Options = { 5 | clean: true, 6 | dts: true, 7 | entry: ['src/index.ts'], 8 | minify: false, 9 | external: Object.keys(dependencies), 10 | sourcemap: true, 11 | target: 'es2022', 12 | tsconfig: 'src/tsconfig.json', 13 | keepNames: true, 14 | banner: { 15 | js: '"use client";' 16 | } 17 | }; 18 | 19 | export default [ 20 | defineConfig({ 21 | ...baseOptions, 22 | outDir: 'dist/cjs', 23 | format: 'cjs', 24 | outExtension: () => ({ js: '.cjs' }) 25 | }), 26 | defineConfig({ 27 | ...baseOptions, 28 | outDir: 'dist/esm', 29 | format: 'esm', 30 | outExtension: () => ({ js: '.mjs' }) 31 | }) 32 | ]; 33 | -------------------------------------------------------------------------------- /scripts/update-core-index-exports.mjs: -------------------------------------------------------------------------------- 1 | import { readdir, readFile, writeFile } from 'node:fs/promises'; 2 | 3 | const coreIndexTsPath = new URL('../packages/core/src/index.ts', import.meta.url); 4 | let coreIndexTs = await readFile(coreIndexTsPath, 'utf-8'); 5 | 6 | const coreComponentsDirectoryPath = new URL('../packages/core/src/components/', import.meta.url); 7 | const coreComponentsDirectory = await readdir(coreComponentsDirectoryPath); 8 | 9 | const paths = []; 10 | 11 | for (const item of coreComponentsDirectory) { 12 | if (item.startsWith('discord-')) { 13 | const filesInDirectory = await readdir(new URL(`${item}/`, coreComponentsDirectoryPath)); 14 | if (filesInDirectory.length) { 15 | const className = filesInDirectory[0].replace(/\.ts$/, ''); 16 | const fileName = `${className}.js`; 17 | paths.push(`export { ${className} } from './components/${item}/${fileName}';`); 18 | } 19 | } 20 | } 21 | 22 | const exportsStartMarker = '/* EXPORTS START */'; 23 | const exportsEndMarker = '/* EXPORTS END */'; 24 | 25 | const startIndex = coreIndexTs.indexOf(exportsStartMarker); 26 | const endIndex = coreIndexTs.indexOf(exportsEndMarker); 27 | 28 | if (startIndex !== -1 && endIndex !== -1) { 29 | const replaceableContent = coreIndexTs.substring(startIndex, endIndex + exportsEndMarker.length); 30 | const contentToInject = [ 31 | // 32 | exportsStartMarker, 33 | '', 34 | paths.join('\n'), 35 | '', 36 | exportsEndMarker 37 | ].join('\n'); 38 | 39 | coreIndexTs = coreIndexTs.replace(replaceableContent, contentToInject); 40 | } 41 | 42 | await writeFile(coreIndexTsPath, coreIndexTs); 43 | -------------------------------------------------------------------------------- /scripts/update-exports-and-side-effects.mjs: -------------------------------------------------------------------------------- 1 | import { readdir, readFile, writeFile } from 'node:fs/promises'; 2 | 3 | const corePackageJsonPath = new URL('../packages/core/package.json', import.meta.url); 4 | const corePackageJson = JSON.parse(await readFile(corePackageJsonPath, 'utf-8')); 5 | 6 | const coreComponentsDirectoryPath = new URL('../packages/core/src/components/', import.meta.url); 7 | const coreComponentsDirectory = await readdir(coreComponentsDirectoryPath); 8 | 9 | const paths = { 10 | '.': './dist/index.js' 11 | }; 12 | 13 | for (const item of coreComponentsDirectory) { 14 | if (item.startsWith('discord-')) { 15 | const filesInDirectory = await readdir(new URL(`${item}/`, coreComponentsDirectoryPath)); 16 | if (filesInDirectory.length) { 17 | paths[`./${item}.js`] = `./dist/components/${item}/${filesInDirectory[0].replace(/\.ts$/, '.js')}`; 18 | } 19 | } 20 | } 21 | 22 | corePackageJson.exports = paths; 23 | corePackageJson.sideEffects = Object.values(paths); 24 | 25 | await writeFile(corePackageJsonPath, JSON.stringify(corePackageJson, null, '\t')); 26 | -------------------------------------------------------------------------------- /scripts/update-react-index-exports.mjs: -------------------------------------------------------------------------------- 1 | import { readdir, readFile, writeFile } from 'node:fs/promises'; 2 | 3 | const reactIndexTsPath = new URL('../packages/react/src/index.ts', import.meta.url); 4 | let reactIndexTs = await readFile(reactIndexTsPath, 'utf-8'); 5 | 6 | const coreComponentsDirectoryPath = new URL('../packages/core/src/components/', import.meta.url); 7 | const coreComponentsDirectory = await readdir(coreComponentsDirectoryPath); 8 | 9 | const paths = []; 10 | 11 | for (const item of coreComponentsDirectory) { 12 | if (item.startsWith('discord-')) { 13 | const filesInDirectory = await readdir(new URL(`${item}/`, coreComponentsDirectoryPath)); 14 | if (filesInDirectory.length) { 15 | const className = filesInDirectory[0].replace(/\.ts$/, ''); 16 | paths.push(`export const ${className} = createReactComponent('${item}', ReactComponents.${className});`); 17 | } 18 | } 19 | } 20 | 21 | const importsStartMarker = '/* IMPORTS START */'; 22 | const importsEndMarker = '/* IMPORTS END */'; 23 | 24 | const startIndex = reactIndexTs.indexOf(importsStartMarker); 25 | const endIndex = reactIndexTs.indexOf(importsEndMarker); 26 | 27 | if (startIndex !== -1 && endIndex !== -1) { 28 | const replaceableContent = reactIndexTs.substring(startIndex, endIndex + importsEndMarker.length); 29 | const contentToInject = [ 30 | // 31 | importsStartMarker, 32 | '', 33 | paths.join('\n'), 34 | '', 35 | importsEndMarker 36 | ].join('\n'); 37 | 38 | reactIndexTs = reactIndexTs.replace(replaceableContent, contentToInject); 39 | } 40 | 41 | await writeFile(reactIndexTsPath, reactIndexTs); 42 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@sapphire/ts-config", "@sapphire/ts-config/extra-strict", "@sapphire/ts-config/verbatim"], 3 | "compilerOptions": { 4 | "target": "ES2022", 5 | "lib": ["ESNext", "DOM", "DOM.Iterable"], 6 | "incremental": false, 7 | "strictPropertyInitialization": false 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "include": ["packages/", "scripts/", "docs/"], 4 | "exclude": ["dist/", "node_modules/"] 5 | } 6 | --------------------------------------------------------------------------------