├── .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 | [](https://github.com/skyra-project/discord-components/blob/main/LICENSE.md)
7 | [](https://donate.skyra.pw/patreon)
8 |
9 | _Core Package_
10 |
11 | [](https://www.npmjs.com/package/@skyra/discord-components-core)
12 | [](https://www.npmjs.com/package/@skyra/discord-components-core)
13 | [](https://bundlephobia.com/result?p=@skyra/discord-components-core)
14 |
15 | _React Bindings_
16 |
17 | [](https://www.npmjs.com/package/@skyra/discord-components-react)
18 | [](https://www.npmjs.com/package/@skyra/discord-components-react)
19 | [](https://bundlephobia.com/result?p=@skyra/discord-components-react)
20 |
21 | [](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 | 
10 |
11 | **_Compact mode_**
12 |
13 | 
14 |
15 | **_With an embed_**
16 |
17 | 
18 |
19 | ### Light Mode
20 |
21 | **_A normal conversation_**
22 |
23 | 
24 |
25 | **_Compact mode_**
26 |
27 | 
28 |
29 | **_With an embed_**
30 |
31 | 
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``
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 | ${this.cta} ›
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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``;
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 |
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 |
25 |
--------------------------------------------------------------------------------
/packages/documentation/src/components/astro/images/astro.svg:
--------------------------------------------------------------------------------
1 |
21 |
--------------------------------------------------------------------------------
/packages/documentation/src/components/astro/images/htmx.svg:
--------------------------------------------------------------------------------
1 |
2 |
29 |
--------------------------------------------------------------------------------
/packages/documentation/src/components/astro/images/nextjs.svg:
--------------------------------------------------------------------------------
1 |
2 |
37 |
--------------------------------------------------------------------------------
/packages/documentation/src/components/astro/images/nuxt.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/packages/documentation/src/components/astro/images/preact.svg:
--------------------------------------------------------------------------------
1 |
26 |
--------------------------------------------------------------------------------
/packages/documentation/src/components/astro/images/qwik.svg:
--------------------------------------------------------------------------------
1 |
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 |
--------------------------------------------------------------------------------