feat(apps): add local connection brand assets (#12343)

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The app store needs stable provider marks that do not depend on
remote image hosts.
> - The connector layer adds many recognizable services.
> - Local assets must work in light and dark themes and follow the
design token gate.
> - This pull request adds the provider asset set and its rendering
support.
> - The benefit is fast and consistent app identification across the UI.

## Linked Issues or Issue Description

Refs #11965

This is stack 5 of 11. It depends on stack 4 and replaces another
reviewable part of #11965.

## What Changed

- Add local provider logos for the connection catalog.
- Add light and dark asset selection where providers need it.
- Add deterministic fallback behavior and UI tests.
- Document the fixed brand-color allowlist in the token gate.

## Verification

- `pnpm -r typecheck`
- `pnpm check:token-gates`
- `pnpm --filter @paperclipai/ui exec vitest run
src/pages/apps/AppLogo.test.tsx`
- `pnpm build`

## Risks

- A missing asset path can show the deterministic letter fallback.
- Brand SVG colors use the documented asset allowlist because provider
marks cannot use product theme tokens.
- The change does not add a database migration.

> I checked `ROADMAP.md`. This stack continues the existing app
connection work from #11965 and does not duplicate another planned item.

## Model Used

OpenAI Codex, GPT-5. The runtime model ID and context window were not
exposed. The model used reasoning, tool use, and code execution.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Refs #` or (b)
described the issue in this pull request
- [x] I have not referenced internal or instance-local Paperclip issues
or links
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip authored and GitHub committed 2026-08-29 12:08:33 -05:00
1 parent 6244e4cf32
commit 10c4902ffc
92 files changed
+1411 -26

No files matched your search

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#18BFFF"><path d="M11.992 1.966c-.434 0-.87.086-1.28.257L1.779 5.917c-.503.208-.49.908.012 1.116l8.982 3.558a3.266 3.266 0 0 0 2.454 0l8.982-3.558c.503-.196.503-.908.012-1.116l-8.957-3.694a3.255 3.255 0 0 0-1.272-.257zM23.4 8.056a.589.589 0 0 0-.222.045l-10.012 3.877a.612.612 0 0 0-.38.564v8.896a.6.6 0 0 0 .821.552L23.62 18.1a.583.583 0 0 0 .38-.551V8.653a.6.6 0 0 0-.6-.596zM.676 8.095a.644.644 0 0 0-.48.19C.086 8.396 0 8.53 0 8.69v8.355c0 .442.515.737.908.54l6.27-3.006.307-.147 2.969-1.436c.466-.22.43-.908-.061-1.092L.883 8.138a.57.57 0 0 0-.207-.044z"/></svg>

After

Width:  |  Height:  |  Size: 634 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#FFFFFF"><path d="M17.3041 3.541h-3.6718l6.696 16.918H24Zm-10.6082 0L0 20.459h3.7442l1.3693-3.5527h7.0052l1.3693 3.5528h3.7442L10.5363 3.5409Zm-.3712 10.2232 2.2914-5.9456 2.2914 5.9456Z"/></svg>

After

Width:  |  Height:  |  Size: 262 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#191919"><path d="M17.3041 3.541h-3.6718l6.696 16.918H24Zm-10.6082 0L0 20.459h3.7442l1.3693-3.5527h7.0052l1.3693 3.5528h3.7442L10.5363 3.5409Zm-.3712 10.2232 2.2914-5.9456 2.2914 5.9456Z"/></svg>

After

Width:  |  Height:  |  Size: 262 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#64748B" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="15" r="4"/><path d="m11 12 8-8m-3 3 2 2m-5 1 2 2"/></svg>

After

Width:  |  Height:  |  Size: 232 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#F06A6A"><path d="M18.78 12.653c-2.882 0-5.22 2.336-5.22 5.22s2.338 5.22 5.22 5.22 5.22-2.34 5.22-5.22-2.336-5.22-5.22-5.22zm-13.56 0c-2.88 0-5.22 2.337-5.22 5.22s2.338 5.22 5.22 5.22 5.22-2.338 5.22-5.22-2.336-5.22-5.22-5.22zm12-6.525c0 2.883-2.337 5.22-5.22 5.22-2.882 0-5.22-2.337-5.22-5.22 0-2.88 2.338-5.22 5.22-5.22 2.883 0 5.22 2.34 5.22 5.22z"/></svg>

After

Width:  |  Height:  |  Size: 426 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#EE6123"><path d="M13.055 21.26c-1.345.022-2.325-.41-2.386-1.585-.025-.44-.018-.91.002-1.192.137-1.716 1.333-2.95 2.53-3.19 1.482-.294 2.455.38 2.455 2.31 0 1.303-.36 3.618-2.59 3.657h-.016zM11.923 0C5.32 0 0 5.297 0 12.224c0 3.594 1.92 7.062 4.623 9.147.52.4 1.138.367 1.497.02.297-.285.272-.984-.285-1.475-2.16-1.886-3.652-4.76-3.652-7.635 0-5.15 4.58-9.49 9.74-9.49 6.28 0 9.636 5.102 9.636 9.43 0 2.65-1.29 5.84-3.626 7.874.015 0 .493-.942.493-2.784 0-3.13-1.976-4.836-4.28-4.836-1.663 0-2.667.598-3.34 1.152 0-1.272.045-3.652.045-3.652 0-1.572-.54-2.83-2.47-2.86-1.11-.015-1.932.493-2.44 1.647-.18.436-.12.916.254 1.125.3.18.81.046 1.046-.284.165-.21.254-.254.404-.24.24.03.257.405.257.66.014.193.193 2.903.088 9.865C7.98 21.798 9.493 24 13.1 24c1.56 0 2.756-.435 4.493-1.422C20.243 21.08 24 17.758 24 12.128 23.953 5.045 18.265 0 11.933 0"/></svg>

After

Width:  |  Height:  |  Size: 920 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#0061D5"><path d="M.959 5.523c-.54 0-.959.42-.959.899v7.549a4.59 4.59 0 004.613 4.494 4.717 4.717 0 004.135-2.457c.779 1.438 2.337 2.457 4.074 2.457 2.577 0 4.674-2.037 4.674-4.613.06-2.457-2.037-4.495-4.613-4.495-1.738 0-3.295.959-4.074 2.397-.78-1.438-2.338-2.397-4.135-2.397-1.079 0-2.038.36-2.817.899V6.422a.92.92 0 00-.898-.899zM17.602 9.26a.95.95 0 00-.704.158c-.36.3-.479.899-.18 1.318l2.397 3.116-2.396 3.115c-.3.42-.24.96.18 1.26.419.3 1.016.298 1.316-.122l2.039-2.636 2.096 2.697c.3.36.899.419 1.318.12.36-.3.42-.84.121-1.259l-2.338-3.115 2.338-3.057c.3-.419.298-1.018-.121-1.318-.48-.3-1.019-.24-1.318.18l-2.096 2.576-2.04-2.695c-.149-.18-.373-.3-.612-.338zM4.613 11.154c1.558 0 2.817 1.26 2.817 2.758 0 1.558-1.259 2.756-2.817 2.756-1.558 0-2.816-1.198-2.816-2.756 0-1.498 1.258-2.758 2.816-2.758zm8.27 0c1.558 0 2.816 1.26 2.816 2.758-.06 1.558-1.318 2.756-2.816 2.756-1.558 0-2.817-1.198-2.817-2.756 0-1.498 1.259-2.758 2.817-2.758Z"/></svg>

After

Width:  |  Height:  |  Size: 1022 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#FFFFFF"><path d="M18.69 2.319a3.868 3.868 0 0 0-3.108 1.547l-.759 1.007a1.658 1.658 0 0 1-1.313.656H0V21.68h5.296a3.87 3.87 0 0 0 3.108-1.547l.759-1.006a1.656 1.656 0 0 1 1.313-.657H24V2.319h-5.31Zm1.108 11.949h-5.66a3.87 3.87 0 0 0-3.108 1.547l-.759 1.007a1.658 1.658 0 0 1-1.313.656H4.202V9.731h5.661a3.868 3.868 0 0 0 3.107-1.547l.759-1.006a1.658 1.658 0 0 1 1.313-.657h4.771l-.015 7.747Z"/></svg>

After

Width:  |  Height:  |  Size: 468 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#212121"><path d="M18.69 2.319a3.868 3.868 0 0 0-3.108 1.547l-.759 1.007a1.658 1.658 0 0 1-1.313.656H0V21.68h5.296a3.87 3.87 0 0 0 3.108-1.547l.759-1.006a1.656 1.656 0 0 1 1.313-.657H24V2.319h-5.31Zm1.108 11.949h-5.66a3.87 3.87 0 0 0-3.108 1.547l-.759 1.007a1.658 1.658 0 0 1-1.313.656H4.202V9.731h5.661a3.868 3.868 0 0 0 3.107-1.547l.759-1.006a1.658 1.658 0 0 1 1.313-.657h4.771l-.015 7.747Z"/></svg>

After

Width:  |  Height:  |  Size: 468 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#FFCC01"><path d="M21.333 10H24v4h-2.667ZM16 1.335h2.667v21.33H16Zm-5.333 0h2.666v21.33h-2.666ZM0 22.665V1.335h2.667v21.33zm5.333-21.33H8v21.33H5.333Z"/></svg>

After

Width:  |  Height:  |  Size: 226 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#F38020"><path d="M16.5088 16.8447c.1475-.5068.0908-.9707-.1553-1.3154-.2246-.3164-.6045-.499-1.0615-.5205l-8.6592-.1123a.1559.1559 0 0 1-.1333-.0713c-.0283-.042-.0351-.0986-.021-.1553.0278-.084.1123-.1484.2036-.1562l8.7359-.1123c1.0351-.0489 2.1601-.8868 2.5537-1.9136l.499-1.3013c.0215-.0561.0293-.1128.0147-.168-.5625-2.5463-2.835-4.4453-5.5499-4.4453-2.5039 0-4.6284 1.6177-5.3876 3.8614-.4927-.3658-1.1187-.5625-1.794-.499-1.2026.119-2.1665 1.083-2.2861 2.2856-.0283.31-.0069.6128.0635.894C1.5683 13.171 0 14.7754 0 16.752c0 .1748.0142.3515.0352.5273.0141.083.0844.1475.1689.1475h15.9814c.0909 0 .1758-.0645.2032-.1553l.12-.4268zm2.7568-5.5634c-.0771 0-.1611 0-.2383.0112-.0566 0-.1054.0415-.127.0976l-.3378 1.1744c-.1475.5068-.0918.9707.1543 1.3164.2256.3164.6055.498 1.0625.5195l1.8437.1133c.0557 0 .1055.0263.1329.0703.0283.043.0351.1074.0214.1562-.0283.084-.1132.1485-.204.1553l-1.921.1123c-1.041.0488-2.1582.8867-2.5527 1.914l-.1406.3585c-.0283.0713.0215.1416.0986.1416h6.5977c.0771 0 .1474-.0489.169-.126.1122-.4082.1757-.837.1757-1.2803 0-2.6025-2.125-4.727-4.7344-4.727"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#FFFFFF"><path d="M24 14.8598c0 2.1729-1.3757 3.974-3.5903 4.6996l-.0995.0318V17.989c1.3777-.5805 2.1869-1.7275 2.1869-3.1291-.0072-2-1.6087-3.6288-3.6082-3.6699h-.5964l-.1432-.5686c-.7025-2.8996-3.2886-4.9489-6.2721-4.97a6.3915 6.3915 0 0 0-5.811 3.664l-.1828.3757-.4175.0437a4.4311 4.4311 0 0 0-3.3052 2.088c-1.2803 2.0856-.6274 4.8143 1.4583 6.0947v1.6897h-.01l-.149-.0675a5.9402 5.9402 0 0 1-3.3658-4.3494c-.5787-3.2291 1.57-6.3161 4.7991-6.8948a7.8766 7.8766 0 0 1 6.9839-4.149c3.4724.025 6.535 2.28 7.5901 5.5883 2.5789.3366 4.5138 2.5245 4.5327 5.1251zm-15.3176-1.322h.5647a.0656.0656 0 0 0 .0457-.1113L7.084 11.2158l-.0007-.0007a.0656.0656 0 0 0-.0927.0007L4.78 13.4265a.0656.0656 0 0 0 .0477.1113h.5566a.0656.0656 0 0 1 .0657.0656v5.0574c0 .6588.534 1.1928 1.1928 1.1928H9.247a.0656.0656 0 0 0 .0457-.1113l-.33-.33a1.1928 1.1928 0 0 1-.348-.839v-4.97a.0676.0676 0 0 1 .0676-.0655zm9.769 2.5466h.5667a.0655.0655 0 0 0 .0457-.1133l-2.2107-2.2087-.0015-.0015a.0636.0636 0 0 0-.0899.0015L14.551 15.971a.0657.0657 0 0 0 .0457.1133h.5567a.0656.0656 0 0 1 .0656.0656v2.5108c0 .6588.534 1.1928 1.1928 1.1928h2.6063a.0655.0655 0 0 0 .0457-.1113l-.33-.33a1.1928 1.1928 0 0 1-.348-.839V16.15a.0656.0656 0 0 1 .0657-.0656zm-4.8844-1.2743h.5646a.0656.0656 0 0 0 .0477-.1114l-2.2107-2.2027-.0006-.0006a.0656.0656 0 0 0-.0928.0006l-2.2087 2.2068a.0656.0656 0 0 0 .0457.1113h.5626a.0676.0676 0 0 1 .0657.0676v3.7791c0 .6588.534 1.1928 1.1928 1.1928h2.5983a.0656.0656 0 0 0 .0477-.1113l-.332-.33a1.193 1.193 0 0 1-.346-.839v-3.6956c0-.0366.0291-.0665.0657-.0676z"/></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#3448C5"><path d="M24 14.8598c0 2.1729-1.3757 3.974-3.5903 4.6996l-.0995.0318V17.989c1.3777-.5805 2.1869-1.7275 2.1869-3.1291-.0072-2-1.6087-3.6288-3.6082-3.6699h-.5964l-.1432-.5686c-.7025-2.8996-3.2886-4.9489-6.2721-4.97a6.3915 6.3915 0 0 0-5.811 3.664l-.1828.3757-.4175.0437a4.4311 4.4311 0 0 0-3.3052 2.088c-1.2803 2.0856-.6274 4.8143 1.4583 6.0947v1.6897h-.01l-.149-.0675a5.9402 5.9402 0 0 1-3.3658-4.3494c-.5787-3.2291 1.57-6.3161 4.7991-6.8948a7.8766 7.8766 0 0 1 6.9839-4.149c3.4724.025 6.535 2.28 7.5901 5.5883 2.5789.3366 4.5138 2.5245 4.5327 5.1251zm-15.3176-1.322h.5647a.0656.0656 0 0 0 .0457-.1113L7.084 11.2158l-.0007-.0007a.0656.0656 0 0 0-.0927.0007L4.78 13.4265a.0656.0656 0 0 0 .0477.1113h.5566a.0656.0656 0 0 1 .0657.0656v5.0574c0 .6588.534 1.1928 1.1928 1.1928H9.247a.0656.0656 0 0 0 .0457-.1113l-.33-.33a1.1928 1.1928 0 0 1-.348-.839v-4.97a.0676.0676 0 0 1 .0676-.0655zm9.769 2.5466h.5667a.0655.0655 0 0 0 .0457-.1133l-2.2107-2.2087-.0015-.0015a.0636.0636 0 0 0-.0899.0015L14.551 15.971a.0657.0657 0 0 0 .0457.1133h.5567a.0656.0656 0 0 1 .0656.0656v2.5108c0 .6588.534 1.1928 1.1928 1.1928h2.6063a.0655.0655 0 0 0 .0457-.1113l-.33-.33a1.1928 1.1928 0 0 1-.348-.839V16.15a.0656.0656 0 0 1 .0657-.0656zm-4.8844-1.2743h.5646a.0656.0656 0 0 0 .0477-.1114l-2.2107-2.2027-.0006-.0006a.0656.0656 0 0 0-.0928.0006l-2.2087 2.2068a.0656.0656 0 0 0 .0457.1113h.5626a.0676.0676 0 0 1 .0657.0676v3.7791c0 .6588.534 1.1928 1.1928 1.1928h2.5983a.0656.0656 0 0 0 .0477-.1113l-.332-.33a1.193 1.193 0 0 1-.346-.839v-3.6956c0-.0366.0291-.0665.0657-.0676z"/></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#F46A54"><path d="M21.194 0H2.806A2.01 2.01 0 0 0 .8 2v20c0 1.1.903 2 2.006 2h18.388a2.01 2.01 0 0 0 2.006-2v-.933c-.033-1.2-.067-3.7-.067-4.834 0-.633-.468-1.166-1.07-1.166-.668 0-1.103.4-1.437.733-1.003.9-2.508 1.067-3.812.833-.601-.133-1.17-.3-1.638-.6-1.438-.833-2.374-2.4-2.374-4.066 0-1.667.936-3.2 2.374-4.067.502-.3 1.07-.467 1.638-.6 1.27-.233 2.809-.067 3.812.833.367.334.802.734 1.437.734.602 0 1.07-.534 1.07-1.167 0-1.1.034-3.633.067-4.833V2c0-1.1-.903-2-2.006-2Z"/></svg>

After

Width:  |  Height:  |  Size: 552 B

+11
View File
@@ -0,0 +1,11 @@
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_2129_476)">
<path d="M58.2101 18.0354L22.1511 10.6607C19.7868 10.1753 17.5635 11.9916 17.5635 14.4028V31.4851V32.3933V49.4754C17.5635 51.8867 19.7868 53.7032 22.1511 53.2177L58.2101 45.843" stroke="white" stroke-width="1.82758" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M30.3675 4.7266C30.3676 1.33903 33.8302 -0.926498 36.9309 0.370393L37.0785 0.434773L37.0817 0.436653L56.3717 9.34549C58.0646 10.1086 59.1257 11.7994 59.1261 13.6354V21.1824C59.1261 23.9356 56.7812 26.1101 54.0417 25.8981L32.195 24.2675V39.6105L54.0384 37.9799L54.2944 37.9667C56.9179 37.9028 59.1252 40.0214 59.1098 42.6801V50.227C59.1098 52.0772 58.0208 53.7432 56.3587 54.516L56.3559 54.5169L37.0822 63.4096L37.0827 63.41C33.9397 64.8647 30.3675 62.5737 30.3675 59.1206V51.9285C30.2259 51.9844 30.0742 52.0214 29.9149 52.033L19.2542 52.8005C18.3634 52.8645 17.6054 52.1586 17.6052 51.2654V43.3507C17.6053 43.0438 17.6969 42.756 17.8543 42.5137L9.0841 43.1688H9.08083C6.34886 43.3625 4.00001 41.1949 4 38.4526V25.4254C4.0002 22.6713 6.34695 20.4959 9.08742 20.7097H9.08694L17.6631 21.3497C17.6259 21.2173 17.6053 21.0781 17.6052 20.9343V12.5969C17.6052 11.3796 18.6942 10.4511 19.8962 10.6434L30.0475 12.2675C30.1591 12.2854 30.2658 12.3159 30.3675 12.3558V4.7266ZM54.1808 39.8023H54.1774L32.195 41.4432V50.9825L57.2826 45.7388V42.6735L57.2798 42.5184C57.2047 40.924 55.8073 39.6754 54.1808 39.8023ZM17.2528 23.1519C17.4061 23.3917 17.4952 23.6759 17.4953 23.9794V40.1801C17.4953 40.3679 17.4601 40.5478 17.398 40.7148L30.3675 39.7468V24.1307L17.2528 23.1519ZM32.195 22.4348L54.1774 24.0757L54.1808 24.0762L54.3367 24.0842C55.9444 24.1234 57.2985 22.8221 57.2985 21.1824V18.0099L32.195 12.7623V22.4348Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_2129_476">
<rect width="55.3383" height="64" fill="white" transform="translate(4)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

+16
View File
@@ -0,0 +1,16 @@
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_2129_471)">
<g clip-path="url(#clip1_2129_471)">
<path d="M58.5409 18.0442L22.4819 10.6695C20.1176 10.1841 17.8943 12.0003 17.8943 14.4116V31.4939V32.402V49.4842C17.8943 51.8955 20.1176 53.712 22.4819 53.2265L58.5409 45.8518" stroke="black" stroke-width="1.82758" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M30.6983 4.73539C30.6984 1.34782 34.161 -0.917709 37.2617 0.379183L37.4093 0.443562L37.4126 0.445442L56.7025 9.35428C58.3954 10.1174 59.4565 11.8082 59.4569 13.6442V21.1912C59.4569 23.9444 57.112 26.1189 54.3726 25.9069L32.5258 24.2763V39.6193L54.3692 37.9887L54.6252 37.9755C57.2487 37.9116 59.456 40.0302 59.4406 42.6889V50.2358C59.4406 52.086 58.3516 53.7519 56.6895 54.5248L56.6867 54.5257L37.413 63.4183L37.4135 63.4188C34.2705 64.8735 30.6983 62.5825 30.6983 59.1294V51.9373C30.5567 51.9931 30.405 52.0302 30.2458 52.0417L19.585 52.8093C18.6942 52.8733 17.9362 52.1673 17.9361 51.2742V43.3595C17.9361 43.0526 18.0277 42.7648 18.1851 42.5225L9.41491 43.1776H9.41164C6.67967 43.3713 4.33082 41.2037 4.33081 38.4614V25.4342C4.33101 22.6801 6.67776 20.5047 9.41823 20.7185H9.41775L17.9939 21.3585C17.9567 21.2261 17.9361 21.0869 17.9361 20.9431V12.6057C17.9361 11.3884 19.025 10.4599 20.227 10.6522L30.3783 12.2763C30.4899 12.2941 30.5967 12.3247 30.6983 12.3646V4.73539ZM54.5116 39.8111H54.5083L32.5258 41.452V50.9913L57.6135 45.7476V42.6823L57.6106 42.5272C57.5355 40.9328 56.1381 39.6842 54.5116 39.8111ZM17.5836 23.1607C17.7369 23.4005 17.8261 23.6847 17.8261 23.9882V40.1889C17.8261 40.3767 17.7909 40.5566 17.7289 40.7236L30.6983 39.7556V24.1395L17.5836 23.1607ZM32.5258 22.4436L54.5083 24.0845L54.5116 24.085L54.6675 24.093C56.2752 24.1322 57.6293 22.8309 57.6293 21.1912V18.0187L32.5258 12.7711V22.4436Z" fill="black"/>
</g>
</g>
<defs>
<clipPath id="clip0_2129_471">
<rect width="64" height="64" fill="white"/>
</clipPath>
<clipPath id="clip1_2129_471">
<rect width="55.3383" height="64" fill="white" transform="translate(4.33081 0.00878906)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

+8
View File
@@ -0,0 +1,8 @@
<svg width="116" height="28" viewBox="0 0 116 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="28" height="28" rx="4" fill="black" style="fill:black;fill-opacity:1;"/>
<path d="M10.5724 15.2565C10.5724 17.5025 9.6613 19.3778 8.17805 21.1047H11.6319L11.6319 22.7786H6.33459V21.1895C7.95557 19.3566 8.58065 17.8628 8.58065 15.2565L10.5724 15.2565Z" fill="white" style="fill:white;fill-opacity:1;"/>
<path d="M17.4276 15.2565C17.4276 17.5025 18.3387 19.3778 19.822 21.1047H16.3681V22.7786H21.6654V21.1895C20.0444 19.3566 19.4194 17.8628 19.4194 15.2565H17.4276Z" fill="white" style="fill:white;fill-opacity:1;"/>
<path d="M10.5724 12.7435C10.5724 10.4975 9.66131 8.62224 8.17807 6.89532L11.6319 6.89532V5.22137L6.33461 5.22137V6.81056C7.95558 8.64343 8.58066 10.1373 8.58066 12.7435L10.5724 12.7435Z" fill="white" style="fill:white;fill-opacity:1;"/>
<path d="M17.4276 12.7435C17.4276 10.4975 18.3387 8.62224 19.822 6.89532L16.3681 6.89532L16.3681 5.22138L21.6654 5.22138V6.81056C20.0445 8.64343 19.4194 10.1373 19.4194 12.7435H17.4276Z" fill="white" style="fill:white;fill-opacity:1;"/>
<path d="M41.168 20.272C38.227 20.272 36.544 17.875 36.544 13.982C36.544 10.072 38.227 7.658 41.168 7.658C43.514 7.658 45.112 9.273 45.537 12.129L43.055 12.248C42.817 10.667 42.103 9.8 41.168 9.8C39.791 9.8 39.026 11.296 39.026 13.982C39.026 16.651 39.791 18.13 41.168 18.13C42.137 18.13 42.868 17.212 43.106 15.512L45.571 15.631C45.18 18.572 43.582 20.272 41.168 20.272ZM50.9534 20.204C48.3694 20.204 46.6354 18.351 46.6354 15.444C46.6354 12.537 48.3694 10.684 50.9534 10.684C53.5374 10.684 55.2714 12.537 55.2714 15.444C55.2714 18.351 53.5374 20.204 50.9534 20.204ZM50.9534 18.164C52.1774 18.164 52.8744 17.195 52.8744 15.444C52.8744 13.693 52.1774 12.724 50.9534 12.724C49.7124 12.724 49.0324 13.693 49.0324 15.444C49.0324 17.195 49.7124 18.164 50.9534 18.164ZM56.9307 20V10.888H59.0387L59.0897 12.52C59.5657 11.262 60.5687 10.684 61.8437 10.684C63.7987 10.684 64.6827 12.095 64.6827 14.101V20H62.3707V14.628C62.3707 13.268 61.9117 12.639 60.9767 12.639C60.0247 12.639 59.2427 13.336 59.2427 14.73V20H56.9307ZM71.9011 20C69.8611 20 68.9261 19.082 68.9261 17.144V12.724H66.3081V10.888H68.9261V8.746H71.2381V10.888H74.6041V12.724H71.2381V16.923C71.2381 17.773 71.6291 18.164 72.4621 18.164H74.6041V20H71.9011ZM80.5814 20.204C78.0144 20.204 76.2464 18.334 76.2464 15.444C76.2464 12.605 77.9804 10.684 80.5474 10.684C83.0634 10.684 84.8144 12.571 84.8144 15.495V16.073H78.6264C78.7284 17.433 79.4934 18.164 80.6324 18.164C81.4654 18.164 82.0774 17.739 82.3154 17.059L84.6274 17.212C84.0154 19.133 82.6214 20.204 80.5814 20.204ZM78.6604 14.492H82.3494C82.2304 13.319 81.5334 12.724 80.5304 12.724C79.4934 12.724 78.8134 13.336 78.6604 14.492ZM85.8448 20L89.0748 15.342L85.9808 10.888H88.4968L90.3668 13.761L92.2198 10.888H94.7358L91.6928 15.342L94.8888 20H92.3898L90.3668 16.889L88.3608 20H85.8448ZM101.461 20C99.4212 20 98.4862 19.082 98.4862 17.144V12.724H95.8682V10.888H98.4862V8.746H100.798V10.888H104.164V12.724H100.798V16.923C100.798 17.773 101.189 18.164 102.022 18.164H104.164V20H101.461ZM107.813 20C107.813 16.345 109.343 13.013 111.842 10.106H105.892V7.93H114.256V9.987C111.485 13.149 110.346 15.852 110.346 20H107.813Z" fill="black" style="fill:black;fill-opacity:1;"/>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

+8
View File
@@ -0,0 +1,8 @@
<svg width="116" height="28" viewBox="0 0 116 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="28" height="28" rx="4" fill="white" style="fill:white;fill-opacity:1;"/>
<path d="M10.5724 15.2565C10.5724 17.5025 9.6613 19.3778 8.17805 21.1047H11.6319L11.6319 22.7786H6.33459V21.1895C7.95557 19.3566 8.58065 17.8628 8.58065 15.2565L10.5724 15.2565Z" fill="black" style="fill:black;fill-opacity:1;"/>
<path d="M17.4276 15.2565C17.4276 17.5025 18.3387 19.3778 19.822 21.1047H16.3681V22.7786H21.6654V21.1895C20.0444 19.3566 19.4194 17.8628 19.4194 15.2565H17.4276Z" fill="black" style="fill:black;fill-opacity:1;"/>
<path d="M10.5724 12.7435C10.5724 10.4975 9.66131 8.62224 8.17807 6.89532L11.6319 6.89532V5.22137L6.33461 5.22137V6.81056C7.95558 8.64343 8.58066 10.1373 8.58066 12.7435L10.5724 12.7435Z" fill="black" style="fill:black;fill-opacity:1;"/>
<path d="M17.4276 12.7435C17.4276 10.4975 18.3387 8.62224 19.822 6.89532L16.3681 6.89532L16.3681 5.22138L21.6654 5.22138V6.81056C20.0444 8.64343 19.4194 10.1373 19.4194 12.7435H17.4276Z" fill="black" style="fill:black;fill-opacity:1;"/>
<path d="M41.168 20.272C38.227 20.272 36.544 17.875 36.544 13.982C36.544 10.072 38.227 7.658 41.168 7.658C43.514 7.658 45.112 9.273 45.537 12.129L43.055 12.248C42.817 10.667 42.103 9.8 41.168 9.8C39.791 9.8 39.026 11.296 39.026 13.982C39.026 16.651 39.791 18.13 41.168 18.13C42.137 18.13 42.868 17.212 43.106 15.512L45.571 15.631C45.18 18.572 43.582 20.272 41.168 20.272ZM50.9534 20.204C48.3694 20.204 46.6354 18.351 46.6354 15.444C46.6354 12.537 48.3694 10.684 50.9534 10.684C53.5374 10.684 55.2714 12.537 55.2714 15.444C55.2714 18.351 53.5374 20.204 50.9534 20.204ZM50.9534 18.164C52.1774 18.164 52.8744 17.195 52.8744 15.444C52.8744 13.693 52.1774 12.724 50.9534 12.724C49.7124 12.724 49.0324 13.693 49.0324 15.444C49.0324 17.195 49.7124 18.164 50.9534 18.164ZM56.9307 20V10.888H59.0387L59.0897 12.52C59.5657 11.262 60.5687 10.684 61.8437 10.684C63.7987 10.684 64.6827 12.095 64.6827 14.101V20H62.3707V14.628C62.3707 13.268 61.9117 12.639 60.9767 12.639C60.0247 12.639 59.2427 13.336 59.2427 14.73V20H56.9307ZM71.9011 20C69.8611 20 68.9261 19.082 68.9261 17.144V12.724H66.3081V10.888H68.9261V8.746H71.2381V10.888H74.6041V12.724H71.2381V16.923C71.2381 17.773 71.6291 18.164 72.4621 18.164H74.6041V20H71.9011ZM80.5814 20.204C78.0144 20.204 76.2464 18.334 76.2464 15.444C76.2464 12.605 77.9804 10.684 80.5474 10.684C83.0634 10.684 84.8144 12.571 84.8144 15.495V16.073H78.6264C78.7284 17.433 79.4934 18.164 80.6324 18.164C81.4654 18.164 82.0774 17.739 82.3154 17.059L84.6274 17.212C84.0154 19.133 82.6214 20.204 80.5814 20.204ZM78.6604 14.492H82.3494C82.2304 13.319 81.5334 12.724 80.5304 12.724C79.4934 12.724 78.8134 13.336 78.6604 14.492ZM85.8448 20L89.0748 15.342L85.9808 10.888H88.4968L90.3668 13.761L92.2198 10.888H94.7358L91.6928 15.342L94.8888 20H92.3898L90.3668 16.889L88.3608 20H85.8448ZM101.461 20C99.4212 20 98.4862 19.082 98.4862 17.144V12.724H95.8682V10.888H98.4862V8.746H100.798V10.888H104.164V12.724H100.798V16.923C100.798 17.773 101.189 18.164 102.022 18.164H104.164V20H101.461ZM107.813 20C107.813 16.345 109.343 13.013 111.842 10.106H105.892V7.93H114.256V9.987C111.485 13.149 110.346 15.852 110.346 20H107.813Z" fill="white" style="fill:white;fill-opacity:1;"/>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#00968F"><path d="M16.742 11.214l-.447-.78a.062.062 0 0 0-.082-.022l-2.014 1.162-1.986-1.167a.062.062 0 0 0-.082.021l-.458.78a.062.062 0 0 0 .021.083l2.009 1.178v2.363c0 .033.027.06.06.06h.89a.06.06 0 0 0 .06-.06v-2.374l2.007-1.162c.028-.016.039-.055.022-.082zm-3.863 1.583c-.017-.028-.055-.038-.088-.022l-1.342.797c-.027.016-.038.054-.022.087l.191.327c.016.028.055.039.087.022l1.343-.797c.027-.016.038-.054.022-.087zm1.533-3.69l-.442.006c-.016 0-.027.011-.027.033l.005 1.62c0 .017.011.028.033.028h.442c.016 0 .027-.01.027-.033l-.005-1.62a.035.035 0 0 0-.033-.033zm2.554 4.377l-1.413-.791c-.017-.005-.033-.005-.039.011l-.218.387c-.005.017-.005.033.011.039l1.413.79c.017.006.033.006.039-.01l.218-.388c.01-.01.005-.032-.011-.038zm-14.057.12h-1.9v-.584h1.687a.075.075 0 0 0 .076-.076v-.65a.075.075 0 0 0-.076-.076H1.01v-.556h1.87a.075.075 0 0 0 .077-.077v-.715a.075.075 0 0 0-.077-.076H.076A.075.075 0 0 0 0 10.87v3.526c0 .043.033.076.076.076H2.91a.075.075 0 0 0 .076-.076v-.715a.078.078 0 0 0-.076-.077zm3.967-1.282H5.26v.748h.725v.442c0 .005 0 .01-.005.01-.153.099-.338.148-.584.148-.584 0-.999-.426-.999-1.02v-.012c0-.562.41-1.01.939-1.01.338 0 .584.11.83.301.032.027.081.022.103-.01l.502-.607a.078.078 0 0 0-.01-.109c-.377-.305-.82-.485-1.436-.485-1.14 0-1.98.84-1.98 1.915v.01c0 1.114.856 1.905 2.002 1.905a2.49 2.49 0 0 0 1.572-.545.088.088 0 0 0 .027-.06v-1.545c.005-.043-.027-.076-.071-.076zm4.044-1.533h-.857a.075.075 0 0 0-.077.076v1.866l-1.49-1.915a.088.088 0 0 0-.06-.027h-.834a.075.075 0 0 0-.077.076v3.525c0 .044.033.077.077.077h.856a.075.075 0 0 0 .077-.077v-1.943l1.55 1.992a.088.088 0 0 0 .06.028h.78a.075.075 0 0 0 .076-.077v-3.525c-.005-.038-.043-.076-.081-.076zm9.571 0H17.42a.075.075 0 0 0-.077.076v.742c0 .044.033.076.077.076h1.026v2.713c0 .043.032.076.076.076h.868a.075.075 0 0 0 .076-.076v-2.702c0-.005.006-.01.011-.01h1.015a.075.075 0 0 0 .077-.077v-.742a.078.078 0 0 0-.077-.076zm3.433 2.815h-1.9v-.584h1.692a.075.075 0 0 0 .077-.076v-.65a.075.075 0 0 0-.077-.076h-1.691v-.556h1.871a.075.075 0 0 0 .077-.077v-.715a.075.075 0 0 0-.077-.076h-2.805a.075.075 0 0 0-.076.076v3.526c0 .043.033.076.076.076h2.833a.075.075 0 0 0 .076-.076v-.715a.075.075 0 0 0-.076-.077Z"/></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="156" height="156" viewBox="0 0 156 156" fill="none"><rect width="156" height="156" rx="40" fill="#050B2C"></rect><path d="M30 108.71L89.3674 128L52.6764 77.4977L30 108.71Z" fill="white"></path><path d="M30 46.2897L52.6764 77.4978L89.3674 27L30 46.2897Z" fill="white"></path><path d="M89.3677 27V128L126.054 77.4978L89.3677 27Z" fill="white"></path></svg>

After

Width:  |  Height:  |  Size: 401 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#FFFFFF"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>

After

Width:  |  Height:  |  Size: 806 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#181717"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>

After

Width:  |  Height:  |  Size: 806 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#EA4335"><path d="M24 5.457v13.909c0 .904-.732 1.636-1.636 1.636h-3.819V11.73L12 16.64l-6.545-4.91v9.273H1.636A1.636 1.636 0 0 1 0 19.366V5.457c0-2.023 2.309-3.178 3.927-1.964L5.455 4.64 12 9.548l6.545-4.91 1.528-1.145C21.69 2.28 24 3.434 24 5.457z"/></svg>

After

Width:  |  Height:  |  Size: 324 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#4285F4" d="M3 4h18v17H3V4Z"/><path fill="#fff" d="M5.5 8h13v10.5h-13V8Z"/><path fill="#EA4335" d="M3 4h18v4H3V4Z"/><path fill="#4285F4" d="M9.2 10.2h5.9v6.2H9.2v-1.5h3.9v-1H9.8v-1.4h3.3v-.9H9.2v-1.4Z"/></svg>

After

Width:  |  Height:  |  Size: 282 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#34A853" d="M3 3h18v14H9l-6 5V3Z"/><path fill="#fff" d="M7 7h10v2H7V7Zm0 4h7v2H7v-2Z"/></svg>

After

Width:  |  Height:  |  Size: 166 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#4285F4"><path d="M5 1h9l5 5v17H5V1Zm9 1.8V7h4.2L14 2.8ZM8 11v1.8h8V11H8Zm0 4v1.8h8V15H8Zm0 4v1.8h6V19H8Z"/></svg>

After

Width:  |  Height:  |  Size: 181 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#4285F4" d="M8.2 2h7.6l6.1 10.6-3.8 6.5H5.9l-3.8-6.5L8.2 2Z"/><path fill="#34A853" d="M8.2 2 2.1 12.6h7.5L15.8 2H8.2Z"/><path fill="#FBBC04" d="m9.6 12.6-3.7 6.5h12.2l3.8-6.5H9.6Z"/></svg>

After

Width:  |  Height:  |  Size: 261 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><circle cx="12" cy="8" r="4" fill="#4285F4"/><path fill="#34A853" d="M4 21c0-5 3.6-8 8-8s8 3 8 8H4Z"/></svg>

After

Width:  |  Height:  |  Size: 169 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#34A853"><path d="M11.318 12.545H7.91v-1.909h3.41v1.91zM14.728 0v6h6l-6-6zm1.363 10.636h-3.41v1.91h3.41v-1.91zm0 3.273h-3.41v1.91h3.41v-1.91zM20.727 6.5v15.864c0 .904-.732 1.636-1.636 1.636H4.909a1.636 1.636 0 0 1-1.636-1.636V1.636C3.273.732 4.005 0 4.909 0h9.318v6.5h6.5zm-3.273 2.773H6.545v7.909h10.91v-7.91zm-6.136 4.636H7.91v1.91h3.41v-1.91z"/></svg>

After

Width:  |  Height:  |  Size: 421 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#FBBC04"><path d="M5 1h9l5 5v17H5V1Zm9 1.8V7h4.2L14 2.8ZM8 10v8h8v-8H8Zm1.8 1.8h4.4v4.4H9.8v-4.4Z"/></svg>

After

Width:  |  Height:  |  Size: 173 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><circle cx="10.5" cy="10.5" r="7" fill="none" stroke="#4285F4" stroke-width="3"/><path stroke="#34A853" stroke-width="3" stroke-linecap="round" d="m15.5 15.5 5 5"/><path stroke="#FBBC04" stroke-width="3" d="M5.5 6a7 7 0 0 1 5-2.5"/><path stroke="#EA4335" stroke-width="3" d="M4 10.5A7 7 0 0 1 5.5 6"/></svg>

After

Width:  |  Height:  |  Size: 368 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#FFD21E"><path d="M12.025 1.13c-5.77 0-10.449 4.647-10.449 10.378 0 1.112.178 2.181.503 3.185.064-.222.203-.444.416-.577a.96.96 0 0 1 .524-.15c.293 0 .584.124.84.284.278.173.48.408.71.694.226.282.458.611.684.951v-.014c.017-.324.106-.622.264-.874s.403-.487.762-.543c.3-.047.596.06.787.203s.31.313.4.467c.15.257.212.468.233.542.01.026.653 1.552 1.657 2.54.616.605 1.01 1.223 1.082 1.912.055.537-.096 1.059-.38 1.572.637.121 1.294.187 1.967.187.657 0 1.298-.063 1.921-.178-.287-.517-.44-1.041-.384-1.581.07-.69.465-1.307 1.081-1.913 1.004-.987 1.647-2.513 1.657-2.539.021-.074.083-.285.233-.542.09-.154.208-.323.4-.467a1.08 1.08 0 0 1 .787-.203c.359.056.604.29.762.543s.247.55.265.874v.015c.225-.34.457-.67.683-.952.23-.286.432-.52.71-.694.257-.16.547-.284.84-.285a.97.97 0 0 1 .524.151c.228.143.373.388.43.625l.006.04a10.3 10.3 0 0 0 .534-3.273c0-5.731-4.678-10.378-10.449-10.378M8.327 6.583a1.5 1.5 0 0 1 .713.174 1.487 1.487 0 0 1 .617 2.013c-.183.343-.762-.214-1.102-.094-.38.134-.532.914-.917.71a1.487 1.487 0 0 1 .69-2.803m7.486 0a1.487 1.487 0 0 1 .689 2.803c-.385.204-.536-.576-.916-.71-.34-.12-.92.437-1.103.094a1.487 1.487 0 0 1 .617-2.013 1.5 1.5 0 0 1 .713-.174m-10.68 1.55a.96.96 0 1 1 0 1.921.96.96 0 0 1 0-1.92m13.838 0a.96.96 0 1 1 0 1.92.96.96 0 0 1 0-1.92M8.489 11.458c.588.01 1.965 1.157 3.572 1.164 1.607-.007 2.984-1.155 3.572-1.164.196-.003.305.12.305.454 0 .886-.424 2.328-1.563 3.202-.22-.756-1.396-1.366-1.63-1.32q-.011.001-.02.006l-.044.026-.01.008-.03.024q-.018.017-.035.036l-.032.04a1 1 0 0 0-.058.09l-.014.025q-.049.088-.11.19a1 1 0 0 1-.083.116 1.2 1.2 0 0 1-.173.18q-.035.029-.075.058a1.3 1.3 0 0 1-.251-.243 1 1 0 0 1-.076-.107c-.124-.193-.177-.363-.337-.444-.034-.016-.104-.008-.2.022q-.094.03-.216.087-.06.028-.125.063l-.13.074q-.067.04-.136.086a3 3 0 0 0-.135.096 3 3 0 0 0-.26.219 2 2 0 0 0-.12.121 2 2 0 0 0-.106.128l-.002.002a2 2 0 0 0-.09.132l-.001.001a1.2 1.2 0 0 0-.105.212q-.013.036-.024.073c-1.139-.875-1.563-2.317-1.563-3.203 0-.334.109-.457.305-.454m.836 10.354c.824-1.19.766-2.082-.365-3.194-1.13-1.112-1.789-2.738-1.789-2.738s-.246-.945-.806-.858-.97 1.499.202 2.362c1.173.864-.233 1.45-.685.64-.45-.812-1.683-2.896-2.322-3.295s-1.089-.175-.938.647 2.822 2.813 2.562 3.244-1.176-.506-1.176-.506-2.866-2.567-3.49-1.898.473 1.23 2.037 2.16c1.564.932 1.686 1.178 1.464 1.53s-3.675-2.511-4-1.297c-.323 1.214 3.524 1.567 3.287 2.405-.238.839-2.71-1.587-3.216-.642-.506.946 3.49 2.056 3.522 2.064 1.29.33 4.568 1.028 5.713-.624m5.349 0c-.824-1.19-.766-2.082.365-3.194 1.13-1.112 1.789-2.738 1.789-2.738s.246-.945.806-.858.97 1.499-.202 2.362c-1.173.864.233 1.45.685.64.451-.812 1.683-2.896 2.322-3.295s1.089-.175.938.647-2.822 2.813-2.562 3.244 1.176-.506 1.176-.506 2.866-2.567 3.49-1.898-.473 1.23-2.037 2.16c-1.564.932-1.686 1.178-1.464 1.53s3.675-2.511 4-1.297c.323 1.214-3.524 1.567-3.287 2.405.238.839 2.71-1.587 3.216-.642.506.946-3.49 2.056-3.522 2.064-1.29.33-4.568 1.028-5.713-.624"/></svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#FFFFFF"><path d="M11.571 11.513H0a5.218 5.218 0 0 0 5.232 5.215h2.13v2.057A5.215 5.215 0 0 0 12.575 24V12.518a1.005 1.005 0 0 0-1.005-1.005zm5.723-5.756H5.736a5.215 5.215 0 0 0 5.215 5.214h2.129v2.058a5.218 5.218 0 0 0 5.215 5.214V6.758a1.001 1.001 0 0 0-1.001-1.001zM23.013 0H11.455a5.215 5.215 0 0 0 5.215 5.215h2.129v2.057A5.215 5.215 0 0 0 24 12.483V1.005A1.001 1.001 0 0 0 23.013 0Z"/></svg>

After

Width:  |  Height:  |  Size: 464 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#0052CC"><path d="M11.571 11.513H0a5.218 5.218 0 0 0 5.232 5.215h2.13v2.057A5.215 5.215 0 0 0 12.575 24V12.518a1.005 1.005 0 0 0-1.005-1.005zm5.723-5.756H5.736a5.215 5.215 0 0 0 5.215 5.214h2.129v2.058a5.218 5.218 0 0 0 5.215 5.214V6.758a1.001 1.001 0 0 0-1.001-1.001zM23.013 0H11.455a5.215 5.215 0 0 0 5.215 5.215h2.129v2.057A5.215 5.215 0 0 0 24 12.483V1.005A1.001 1.001 0 0 0 23.013 0Z"/></svg>

After

Width:  |  Height:  |  Size: 464 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="76" height="96" viewBox="0 0 76 96" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M34.6641 50.0723C36.5361 50.0723 38.1311 50.693 39.4727 51.9453L39.7373 52.2041L39.7432 52.21C41.1624 53.6114 41.8671 55.3017 41.8672 57.3096V88.2617C41.8672 90.2698 41.1622 91.9749 39.7402 93.4033C38.3459 94.8039 36.6635 95.4999 34.6641 95.5C32.6725 95.5 30.9945 94.7969 29.6016 93.3789L16.625 78.5273L16.5068 78.4121C15.889 77.8962 14.9065 78.3308 14.9062 79.1758V88.2617C14.9062 90.2699 14.2013 91.9749 12.7793 93.4033C11.3849 94.8039 9.70264 95.4999 7.70312 95.5C5.82826 95.5 4.21931 94.8882 2.85449 93.6553L2.58496 93.4004C1.19174 91.9739 0.5 90.2698 0.5 88.2617V57.3096L0.507812 56.9365C0.589123 55.0888 1.28059 53.5203 2.58789 52.207C4.00977 50.7787 5.70608 50.0723 7.70312 50.0723C9.69842 50.0723 11.3913 50.7659 12.8105 52.1631L25.7412 67.002C26.3415 67.6905 27.4607 67.2565 27.4609 66.3545V57.3096C27.461 55.299 28.1553 53.6078 29.5498 52.207C30.9716 50.779 32.6673 50.0723 34.6641 50.0723ZM52.8906 50.0723C54.7626 50.0723 56.3577 50.693 57.6992 51.9453L57.9639 52.2041L57.9697 52.21C59.3889 53.6114 60.0937 55.3017 60.0938 57.3096V79.1699C60.0938 79.9775 60.7457 80.6367 61.5557 80.6367H68.2969C70.2936 80.6368 71.9751 81.3441 73.3701 82.7695L73.376 82.7754C74.7951 84.1768 75.5 85.8671 75.5 87.875C75.5 89.8832 74.795 91.5882 73.373 93.0166C71.9787 94.4171 70.2963 95.1132 68.2969 95.1133H52.8906C50.8937 95.1132 49.1983 94.4191 47.7783 93.0195H47.7793C46.382 91.5917 45.6875 89.8859 45.6875 87.875V57.3096C45.6876 55.299 46.3819 53.6078 47.7764 52.207C49.1982 50.779 50.8939 50.0723 52.8906 50.0723ZM70.0264 50.0723C73.0471 50.0725 75.4997 52.5324 75.5 55.5723C75.5 58.6124 73.0472 61.073 70.0264 61.0732C67.0054 61.0732 64.5527 58.6125 64.5527 55.5723C64.553 52.5323 67.0056 50.0723 70.0264 50.0723ZM7.70312 0.5C9.69973 0.500052 11.3814 1.20662 12.7764 2.63184V2.63281L12.7832 2.63867C14.2021 4.04004 14.9062 5.73048 14.9062 7.73828V11.4756C14.9062 12.414 16.1021 12.8275 16.6719 12.0664L23.1631 3.39551L23.165 3.39258C24.3425 1.79947 25.9031 0.877129 27.875 0.611328H27.8809C29.8524 0.321547 31.6306 0.763471 33.2432 1.94531H33.2441C34.8326 3.15422 35.7527 4.73728 36.0176 6.7207V6.72656C36.3065 8.70983 35.8535 10.4847 34.6504 12.0801L34.6484 12.082L27.9551 21.0586C27.5668 21.5794 27.5673 22.2953 27.9551 22.8164L36.5352 34.3457L36.5371 34.3486C37.7403 35.944 38.1941 37.7181 37.9053 39.7012V39.707C37.6404 41.6902 36.72 43.2725 35.1318 44.4814C33.5189 45.6642 31.7408 46.1073 29.7686 45.8174L29.7627 45.8164L29.3975 45.7588C27.5968 45.4327 26.1556 44.5296 25.0518 43.0361L25.0498 43.0332L16.6709 31.8799L16.5566 31.752C15.9557 31.1752 14.9065 31.5924 14.9062 32.4717V38.6904C14.9062 40.6985 14.2012 42.4037 12.7793 43.832C11.385 45.2324 9.70252 45.9277 7.70312 45.9277C5.70335 45.9277 4.00614 45.2315 2.58496 43.8281H2.58398C1.19131 42.4018 0.500058 40.698 0.5 38.6904V7.73828L0.507812 7.36523C0.589019 5.51742 1.28065 3.94905 2.58789 2.63574C3.92088 1.29672 5.49498 0.59075 7.33203 0.507812L7.70312 0.5ZM48.1309 0.886719H59.6855C63.815 0.886723 67.3212 2.34317 70.2314 5.2666C73.1417 8.19011 74.5918 11.7131 74.5918 15.8633C74.5917 20.0134 73.1417 23.5365 70.2314 26.46C70.1259 26.566 70.0203 26.6691 69.915 26.7695C69.3753 27.2845 69.2612 28.1435 69.7275 28.7695L73.8828 34.3467L73.8838 34.3486C75.0869 35.944 75.5408 37.7181 75.252 39.7012L75.251 39.707C74.9861 41.6903 74.0667 43.2726 72.4785 44.4814C70.8656 45.6642 69.0875 46.1073 67.1152 45.8174L67.1094 45.8164C65.1464 45.5519 63.5915 44.6371 62.416 43.0576L57.166 33.8633C56.663 32.9832 55.3341 33.3511 55.334 34.3525V38.6904C55.3339 40.6986 54.6299 42.4037 53.208 43.832C51.8136 45.2327 50.1305 45.9277 48.1309 45.9277C46.1313 45.9276 44.4338 45.2314 43.0127 43.8281C41.6198 42.4017 40.9278 40.698 40.9277 38.6904V8.125C40.9278 6.11436 41.6221 4.42328 43.0166 3.02246C44.4384 1.59431 46.134 0.886821 48.1309 0.886719ZM57.5742 12.0742C55.569 12.0743 53.9483 13.7082 53.9482 15.7178C53.9482 17.7274 55.569 19.3613 57.5742 19.3613C59.5795 19.3613 61.2002 17.7274 61.2002 15.7178C61.2001 13.7082 59.5794 12.0742 57.5742 12.0742Z" stroke="#1C2024"/>
</svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#5E6AD2"><path d="M2.886 4.18A11.982 11.982 0 0 1 11.99 0C18.624 0 24 5.376 24 12.009c0 3.64-1.62 6.903-4.18 9.105L2.887 4.18ZM1.817 5.626l16.556 16.556c-.524.33-1.075.62-1.65.866L.951 7.277c.247-.575.537-1.126.866-1.65ZM.322 9.163l14.515 14.515c-.71.172-1.443.282-2.195.322L0 11.358a12 12 0 0 1 .322-2.195Zm-.17 4.862 9.823 9.824a12.02 12.02 0 0 1-9.824-9.824Z"/></svg>

After

Width:  |  Height:  |  Size: 437 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#FFFFFF"><path d="M13.38 3.498c-.27 0-.511.19-.566.465L9.85 18.986a.578.578 0 0 0 .453.678l4.095.826a.58.58 0 0 0 .682-.455l2.963-15.021a.578.578 0 0 0-.453-.678l-4.096-.826a.589.589 0 0 0-.113-.012zm-5.876.098a.576.576 0 0 0-.516.318L.062 17.697a.575.575 0 0 0 .256.774l3.733 1.877a.578.578 0 0 0 .775-.258l6.926-13.781a.577.577 0 0 0-.256-.776L7.762 3.658a.571.571 0 0 0-.258-.062zm11.74.115a.576.576 0 0 0-.576.576v15.426c0 .318.258.578.576.578h4.178a.58.58 0 0 0 .578-.578V4.287a.578.578 0 0 0-.578-.576Z"/></svg>

After

Width:  |  Height:  |  Size: 584 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#6D00CC"><path d="M13.38 3.498c-.27 0-.511.19-.566.465L9.85 18.986a.578.578 0 0 0 .453.678l4.095.826a.58.58 0 0 0 .682-.455l2.963-15.021a.578.578 0 0 0-.453-.678l-4.096-.826a.589.589 0 0 0-.113-.012zm-5.876.098a.576.576 0 0 0-.516.318L.062 17.697a.575.575 0 0 0 .256.774l3.733 1.877a.578.578 0 0 0 .775-.258l6.926-13.781a.577.577 0 0 0-.256-.776L7.762 3.658a.571.571 0 0 0-.258-.062zm11.74.115a.576.576 0 0 0-.576.576v15.426c0 .318.258.578.576.578h4.178a.58.58 0 0 0 .578-.578V4.287a.578.578 0 0 0-.578-.576Z"/></svg>

After

Width:  |  Height:  |  Size: 584 B

+598
View File
@@ -0,0 +1,598 @@
{
"schemaVersion": 1,
"verifiedAt": "2026-08-26",
"simpleIconsVersion": "16.28.0",
"providers": [
{
"slug": "airtable",
"provider": "Airtable",
"catalogVisible": true,
"localAsset": "/brands/apps/airtable.svg",
"officialSourceUrl": "https://airtable.com/newsroom",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/airtable.svg",
"assetType": "svg",
"darkVariantRequired": false
},
{
"slug": "anthropic",
"provider": "Anthropic",
"catalogVisible": false,
"localAsset": "/brands/apps/anthropic.svg",
"darkAsset": "/brands/apps/anthropic-dark.svg",
"officialSourceUrl": "https://www.anthropic.com",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/anthropic.svg",
"assetType": "svg",
"darkVariantRequired": true
},
{
"slug": "asana",
"provider": "Asana",
"catalogVisible": true,
"localAsset": "/brands/apps/asana.svg",
"officialSourceUrl": "https://asana.com/brand",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/asana.svg",
"assetType": "svg",
"darkVariantRequired": false
},
{
"slug": "beehiiv",
"provider": "beehiiv",
"catalogVisible": false,
"localAsset": "/brands/apps/beehiiv.png",
"officialSourceUrl": "https://www.beehiiv.com",
"upstreamAssetUrl": "https://beehiiv-marketing-images.s3.amazonaws.com/Redesign2023/favicon.png",
"assetType": "png",
"darkVariantRequired": false
},
{
"slug": "bitly",
"provider": "Bitly",
"catalogVisible": false,
"localAsset": "/brands/apps/bitly.svg",
"officialSourceUrl": "https://bitly.com/pages/press",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/bitly.svg",
"assetType": "svg",
"darkVariantRequired": false
},
{
"slug": "box",
"provider": "Box",
"catalogVisible": true,
"localAsset": "/brands/apps/box.svg",
"officialSourceUrl": "https://www.box.com/en-gb/about-us/press",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/box.svg",
"assetType": "svg",
"darkVariantRequired": false
},
{
"slug": "brex",
"provider": "Brex",
"catalogVisible": false,
"localAsset": "/brands/apps/brex.svg",
"darkAsset": "/brands/apps/brex-dark.svg",
"officialSourceUrl": "https://www.brex.com/journal/press",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/brex.svg",
"assetType": "svg",
"darkVariantRequired": true
},
{
"slug": "candid",
"provider": "Candid",
"catalogVisible": false,
"localAsset": "/brands/apps/candid.png",
"officialSourceUrl": "https://candid.org",
"upstreamAssetUrl": "https://cdn.candid.org/images/candid-logo/candid-logo-square.png",
"assetType": "png",
"darkVariantRequired": false
},
{
"slug": "clickhouse",
"provider": "ClickHouse",
"catalogVisible": true,
"localAsset": "/brands/apps/clickhouse.svg",
"officialSourceUrl": "https://github.com/ClickHouse/ClickHouse/blob/12bd453a43819176d25ecf247033f6cb1af54beb/website/images/logo-clickhouse.svg",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/clickhouse.svg",
"assetType": "svg",
"darkVariantRequired": false
},
{
"slug": "cloudflare",
"provider": "Cloudflare",
"catalogVisible": true,
"localAsset": "/brands/apps/cloudflare.svg",
"officialSourceUrl": "https://www.cloudflare.com/logo/",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/cloudflare.svg",
"assetType": "svg",
"darkVariantRequired": false
},
{
"slug": "cloudinary",
"provider": "Cloudinary",
"catalogVisible": true,
"localAsset": "/brands/apps/cloudinary.svg",
"darkAsset": "/brands/apps/cloudinary-dark.svg",
"officialSourceUrl": "https://cloudinary.com",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/cloudinary.svg",
"assetType": "svg",
"darkVariantRequired": true
},
{
"slug": "coda",
"provider": "Coda",
"catalogVisible": false,
"localAsset": "/brands/apps/coda.svg",
"officialSourceUrl": "https://coda.io",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/coda.svg",
"assetType": "svg",
"darkVariantRequired": false
},
{
"slug": "composio",
"provider": "Composio",
"catalogVisible": false,
"localAsset": "/brands/apps/composio.svg",
"darkAsset": "/brands/apps/composio-dark.svg",
"officialSourceUrl": "https://composio.dev",
"upstreamAssetUrl": "https://composio.dev/logos/composio-black.svg",
"assetType": "svg",
"darkVariantRequired": true
},
{
"slug": "context7",
"provider": "Context7",
"catalogVisible": false,
"localAsset": "/brands/apps/context7.svg",
"darkAsset": "/brands/apps/context7-dark.svg",
"officialSourceUrl": "https://context7.com",
"upstreamAssetUrl": "https://context7.com/_next/static/media/context7-logo-dark.2bd29d7e.svg",
"assetType": "svg",
"darkVariantRequired": true
},
{
"slug": "egnyte",
"provider": "Egnyte",
"catalogVisible": false,
"localAsset": "/brands/apps/egnyte.svg",
"officialSourceUrl": "https://www.egnyte.com/presskit.html",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/egnyte.svg",
"assetType": "svg",
"darkVariantRequired": false
},
{
"slug": "embat",
"provider": "Embat",
"catalogVisible": false,
"localAsset": "/brands/apps/embat.svg",
"officialSourceUrl": "https://www.embat.io",
"upstreamAssetUrl": "https://www.embat.io/favicon.svg",
"assetType": "svg",
"darkVariantRequired": false
},
{
"slug": "github",
"provider": "GitHub",
"catalogVisible": false,
"localAsset": "/brands/apps/github.svg",
"darkAsset": "/brands/apps/github-dark.svg",
"officialSourceUrl": "https://github.com/logos",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/github.svg",
"assetType": "svg",
"darkVariantRequired": true
},
{
"slug": "gmail",
"provider": "Gmail",
"catalogVisible": true,
"localAsset": "/brands/apps/gmail.svg",
"officialSourceUrl": "https://fonts.gstatic.com/s/i/productlogos/gmail_2020q4/v8/192px.svg",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/gmail.svg",
"assetType": "svg",
"darkVariantRequired": false
},
{
"slug": "google-sheets",
"provider": "Google Sheets",
"catalogVisible": true,
"localAsset": "/brands/apps/google-sheets.svg",
"officialSourceUrl": "https://sheets.google.com",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/googlesheets.svg",
"assetType": "svg",
"darkVariantRequired": false
},
{
"slug": "google-drive", "provider": "Google Drive", "catalogVisible": true,
"localAsset": "/brands/apps/google-drive.svg", "officialSourceUrl": "https://drive.google.com",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/googledrive.svg",
"assetType": "svg", "darkVariantRequired": false
},
{
"slug": "google-docs", "provider": "Google Docs", "catalogVisible": true,
"localAsset": "/brands/apps/google-docs.svg", "officialSourceUrl": "https://docs.google.com",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/googledocs.svg",
"assetType": "svg", "darkVariantRequired": false
},
{
"slug": "google-slides", "provider": "Google Slides", "catalogVisible": true,
"localAsset": "/brands/apps/google-slides.svg", "officialSourceUrl": "https://slides.google.com",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/googleslides.svg",
"assetType": "svg", "darkVariantRequired": false
},
{
"slug": "google-calendar", "provider": "Google Calendar", "catalogVisible": true,
"localAsset": "/brands/apps/google-calendar.svg", "officialSourceUrl": "https://calendar.google.com",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/googlecalendar.svg",
"assetType": "svg", "darkVariantRequired": false
},
{
"slug": "google-chat", "provider": "Google Chat", "catalogVisible": true,
"localAsset": "/brands/apps/google-chat.svg", "officialSourceUrl": "https://chat.google.com",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/googlechat.svg",
"assetType": "svg", "darkVariantRequired": false
},
{
"slug": "google-people", "provider": "Google People", "catalogVisible": true,
"localAsset": "/brands/apps/google-people.svg", "officialSourceUrl": "https://contacts.google.com",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/googlecontacts.svg",
"assetType": "svg", "darkVariantRequired": false
},
{
"slug": "google-workspace-search", "provider": "Google Workspace Search", "catalogVisible": true,
"localAsset": "/brands/apps/google-workspace-search.svg", "officialSourceUrl": "https://workspace.google.com",
"upstreamAssetUrl": "https://fonts.google.com/icons?selected=Material+Symbols+Outlined:search:FILL@0;wght@400;GRAD@0;opsz@24",
"assetType": "svg", "darkVariantRequired": false
},
{
"slug": "hugging-face",
"provider": "Hugging Face",
"catalogVisible": true,
"localAsset": "/brands/apps/hugging-face.svg",
"officialSourceUrl": "https://huggingface.co/brand",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/huggingface.svg",
"assetType": "svg",
"darkVariantRequired": false
},
{
"slug": "jira",
"provider": "Jira",
"catalogVisible": true,
"localAsset": "/brands/apps/jira.svg",
"darkAsset": "/brands/apps/jira-dark.svg",
"officialSourceUrl": "https://atlassian.design/resources/logo-library",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/jira.svg",
"assetType": "svg",
"darkVariantRequired": true
},
{
"slug": "kernel",
"provider": "Kernel",
"catalogVisible": false,
"localAsset": "/brands/apps/kernel.svg",
"officialSourceUrl": "https://www.kernel.sh",
"upstreamAssetUrl": "https://www.kernel.sh/rebrand/logos/logo-kernel-square.svg",
"assetType": "svg",
"darkVariantRequired": false
},
{
"slug": "linear",
"provider": "Linear",
"catalogVisible": true,
"localAsset": "/brands/apps/linear.svg",
"officialSourceUrl": "https://linear.app",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/linear.svg",
"assetType": "svg",
"darkVariantRequired": false
},
{
"slug": "local-falcon",
"provider": "Local Falcon",
"catalogVisible": false,
"localAsset": "/brands/apps/local-falcon.png",
"officialSourceUrl": "https://www.localfalcon.com",
"upstreamAssetUrl": "https://www.localfalcon.com/uploads/ico/android-chrome-192x192.png",
"assetType": "png",
"darkVariantRequired": false
},
{
"slug": "make",
"provider": "Make",
"catalogVisible": false,
"localAsset": "/brands/apps/make.svg",
"darkAsset": "/brands/apps/make-dark.svg",
"officialSourceUrl": "https://www.make.com/en/press",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/make.svg",
"assetType": "svg",
"darkVariantRequired": true
},
{
"slug": "manufact",
"provider": "Manufact",
"catalogVisible": false,
"localAsset": "/brands/apps/manufact.svg",
"officialSourceUrl": "https://manufact.com",
"upstreamAssetUrl": "https://manufact.com/favicon.svg",
"assetType": "svg",
"darkVariantRequired": false
},
{
"slug": "mem0",
"provider": "Mem0",
"catalogVisible": true,
"localAsset": "/brands/apps/mem0.svg",
"officialSourceUrl": "https://mem0.ai",
"upstreamAssetUrl": "https://framerusercontent.com/images/2ys67ADJdvcyGmQnhp8vKWSq8.svg",
"assetType": "svg",
"darkVariantRequired": false
},
{
"slug": "miro",
"provider": "Miro",
"catalogVisible": true,
"localAsset": "/brands/apps/miro.svg",
"darkAsset": "/brands/apps/miro-dark.svg",
"officialSourceUrl": "https://miro.com",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/miro.svg",
"assetType": "svg",
"darkVariantRequired": true
},
{
"slug": "mixpanel",
"provider": "Mixpanel",
"catalogVisible": true,
"localAsset": "/brands/apps/mixpanel.svg",
"officialSourceUrl": "https://mixpanel.com",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/mixpanel.svg",
"assetType": "svg",
"darkVariantRequired": false
},
{
"slug": "netlify",
"provider": "Netlify",
"catalogVisible": true,
"localAsset": "/brands/apps/netlify.svg",
"officialSourceUrl": "https://www.netlify.com/press/",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/netlify.svg",
"assetType": "svg",
"darkVariantRequired": false
},
{
"slug": "notion",
"provider": "Notion",
"catalogVisible": true,
"localAsset": "/brands/apps/notion.svg",
"darkAsset": "/brands/apps/notion-dark.svg",
"officialSourceUrl": "https://www.notion.so",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/notion.svg",
"assetType": "svg",
"darkVariantRequired": true
},
{
"slug": "oreilly",
"provider": "O'Reilly",
"catalogVisible": false,
"localAsset": "/brands/apps/oreilly.svg",
"officialSourceUrl": "https://www.oreilly.com/about/logos/",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/oreilly.svg",
"assetType": "svg",
"darkVariantRequired": false
},
{
"slug": "pagerduty",
"provider": "PagerDuty",
"catalogVisible": true,
"localAsset": "/brands/apps/pagerduty.svg",
"officialSourceUrl": "https://www.pagerduty.com/brand/",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/pagerduty.svg",
"assetType": "svg",
"darkVariantRequired": false
},
{
"slug": "planetscale",
"provider": "PlanetScale",
"catalogVisible": false,
"localAsset": "/brands/apps/planetscale.svg",
"darkAsset": "/brands/apps/planetscale-dark.svg",
"officialSourceUrl": "https://planetscale.com",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/planetscale.svg",
"assetType": "svg",
"darkVariantRequired": true
},
{
"slug": "posthog",
"provider": "PostHog",
"catalogVisible": true,
"localAsset": "/brands/apps/posthog.svg",
"darkAsset": "/brands/apps/posthog-dark.svg",
"officialSourceUrl": "https://posthog.com/handbook/company/brand-assets",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/posthog.svg",
"assetType": "svg",
"darkVariantRequired": true
},
{
"slug": "postman",
"provider": "Postman",
"catalogVisible": true,
"localAsset": "/brands/apps/postman.svg",
"officialSourceUrl": "https://www.getpostman.com/resources/media-assets/",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/postman.svg",
"assetType": "svg",
"darkVariantRequired": false
},
{
"slug": "razorpay",
"provider": "Razorpay",
"catalogVisible": false,
"localAsset": "/brands/apps/razorpay.svg",
"darkAsset": "/brands/apps/razorpay-dark.svg",
"officialSourceUrl": "https://razorpay.com/newsroom/brand-assets/",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/razorpay.svg",
"assetType": "svg",
"darkVariantRequired": true
},
{
"slug": "resend",
"provider": "Resend",
"catalogVisible": true,
"localAsset": "/brands/apps/resend.svg",
"darkAsset": "/brands/apps/resend-dark.svg",
"officialSourceUrl": "https://resend.com/brand",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/resend.svg",
"assetType": "svg",
"darkVariantRequired": true
},
{
"slug": "sanity",
"provider": "Sanity",
"catalogVisible": false,
"localAsset": "/brands/apps/sanity.svg",
"darkAsset": "/brands/apps/sanity-dark.svg",
"officialSourceUrl": "https://github.com/sanity-io/logos/blob/e298a1ac2757c83910fe08b496a115bc9aef06bd/src/sanityMonogram.tsx",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/sanity.svg",
"assetType": "svg",
"darkVariantRequired": true
},
{
"slug": "sentry",
"provider": "Sentry",
"catalogVisible": true,
"localAsset": "/brands/apps/sentry.svg",
"darkAsset": "/brands/apps/sentry-dark.svg",
"officialSourceUrl": "https://sentry.io/branding/",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/sentry.svg",
"assetType": "svg",
"darkVariantRequired": true
},
{
"slug": "shopify",
"provider": "Shopify",
"catalogVisible": true,
"localAsset": "/brands/apps/shopify.svg",
"officialSourceUrl": "https://www.shopify.com/brand-assets",
"upstreamAssetUrl": "https://cdn.shopify.com/static/brand-assets/shopify-shopping-bag.zip",
"assetType": "svg",
"darkVariantRequired": false
},
{
"slug": "similarweb",
"provider": "Similarweb",
"catalogVisible": false,
"localAsset": "/brands/apps/similarweb.svg",
"darkAsset": "/brands/apps/similarweb-dark.svg",
"officialSourceUrl": "https://www.similarweb.com",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/similarweb.svg",
"assetType": "svg",
"darkVariantRequired": true
},
{
"slug": "slack",
"provider": "Slack",
"catalogVisible": false,
"localAsset": "/brands/apps/slack.png",
"officialSourceUrl": "https://slack.com/brand-guidelines",
"upstreamAssetUrl": "https://a.slack-edge.com/80588/marketing/img/meta/slack_hash_256.png",
"assetType": "png",
"darkVariantRequired": false
},
{
"slug": "stripe",
"provider": "Stripe",
"catalogVisible": true,
"localAsset": "/brands/apps/stripe.svg",
"officialSourceUrl": "https://stripe.com/newsroom/information",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/stripe.svg",
"assetType": "svg",
"darkVariantRequired": false
},
{
"slug": "supabase",
"provider": "Supabase",
"catalogVisible": true,
"localAsset": "/brands/apps/supabase.svg",
"officialSourceUrl": "https://github.com/supabase/supabase/blob/4031a7549f5d46da7bc79c01d56be4177dc7c114/packages/common/assets/images/supabase-logo-wordmark--light.svg",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/supabase.svg",
"assetType": "svg",
"darkVariantRequired": false
},
{
"slug": "ticket-tailor",
"provider": "Ticket Tailor",
"catalogVisible": false,
"localAsset": "/brands/apps/ticket-tailor.svg",
"darkAsset": "/brands/apps/ticket-tailor-dark.svg",
"officialSourceUrl": "https://www.tickettailor.com",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/tickettailor.svg",
"assetType": "svg",
"darkVariantRequired": true
},
{
"slug": "ticktick",
"provider": "TickTick",
"catalogVisible": false,
"localAsset": "/brands/apps/ticktick.svg",
"officialSourceUrl": "https://ticktick.com",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/ticktick.svg",
"assetType": "svg",
"darkVariantRequired": false
},
{
"slug": "todoist",
"provider": "Todoist",
"catalogVisible": true,
"localAsset": "/brands/apps/todoist.svg",
"officialSourceUrl": "https://doist.com/press",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/todoist.svg",
"assetType": "svg",
"darkVariantRequired": false
},
{
"slug": "vercel",
"provider": "Vercel",
"catalogVisible": false,
"localAsset": "/brands/apps/vercel.svg",
"darkAsset": "/brands/apps/vercel-dark.svg",
"officialSourceUrl": "https://vercel.com/geist/brands",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/vercel.svg",
"assetType": "svg",
"darkVariantRequired": true
},
{
"slug": "webflow",
"provider": "Webflow",
"catalogVisible": true,
"localAsset": "/brands/apps/webflow.svg",
"officialSourceUrl": "https://brand-at.webflow.io/resources#logos",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/webflow.svg",
"assetType": "svg",
"darkVariantRequired": false
},
{
"slug": "wix",
"provider": "Wix",
"catalogVisible": true,
"localAsset": "/brands/apps/wix.svg",
"officialSourceUrl": "https://www.wix.com/about/design-assets",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/wix.svg",
"assetType": "svg",
"darkVariantRequired": false
},
{
"slug": "xero",
"provider": "Xero",
"catalogVisible": false,
"localAsset": "/brands/apps/xero.svg",
"officialSourceUrl": "https://www.xero.com/uk/about/media/downloads",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/xero.svg",
"assetType": "svg",
"darkVariantRequired": false
},
{
"slug": "zapier",
"provider": "Zapier",
"catalogVisible": true,
"localAsset": "/brands/apps/zapier.svg",
"officialSourceUrl": "https://www.figma.com/file/NQFxTCE5pGR3dHZt0DkOyy/Zapier-Brand-Guidelines-%5BExternal%5D?type=design&node-id=101-9701&mode=design",
"upstreamAssetUrl": "https://github.com/simple-icons/simple-icons/blob/16.28.0/icons/zapier.svg",
"assetType": "svg",
"darkVariantRequired": false
}
]
}
+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="1000" height="1000"><style>
#light-icon {
display: inline;
}
#dark-icon {
display: none;
}
@media (prefers-color-scheme: dark) {
#light-icon {
display: none;
}
#dark-icon {
display: inline;
}
}
</style><g id="light-icon"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="1000" height="1000"><g><g transform="matrix(2,0,0,2,0,0)"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="500" height="500"><svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M105.933 0C164.437 0.000115889 211.865 47.607 211.865 106.333C211.865 131.828 210.494 158.401 221.068 181.6L228.976 198.947C243.585 230.997 269.266 256.7 301.304 271.336L316.156 278.121C340.143 289.079 367.695 287.335 394.067 287.335C452.572 287.335 500 334.942 500 393.668C500 452.394 452.572 500.001 394.067 500.001C335.563 500.001 288.135 452.394 288.135 393.668C288.135 368.974 289.241 343.275 278.992 320.807L270.587 302.38C255.949 270.289 230.214 244.565 198.118 229.939L180.164 221.758C157.282 211.331 131.078 212.666 105.933 212.666C47.4278 212.666 4.92992e-05 165.059 0 106.333C0 47.607 47.4278 0 105.933 0Z" fill="black"></path>
<circle cx="100.426" cy="399.575" r="100.426" fill="black"></circle>
<path d="M500 100.426C500 155.889 455.037 200.851 399.574 200.851C344.11 200.851 299.148 155.889 299.148 100.426C299.148 44.962 344.11 0 399.574 0C455.037 0 500 44.962 500 100.426Z" fill="black"></path>
</svg></svg></g></g></svg></g><g id="dark-icon"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="1000" height="1000"><g><g transform="matrix(2,0,0,2,0,0)"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="500" height="500" viewBox="0 0 500 500"><svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="500" height="500" rx="110" fill="black"/>
<path d="M105.933 0C164.437 0.000115889 211.865 47.607 211.865 106.333C211.865 131.828 210.494 158.401 221.068 181.6L228.976 198.947C243.585 230.997 269.266 256.7 301.304 271.336L316.156 278.121C340.143 289.079 367.695 287.335 394.067 287.335C452.572 287.335 500 334.942 500 393.668C500 452.394 452.572 500.001 394.067 500.001C335.563 500.001 288.135 452.394 288.135 393.668C288.135 368.974 289.241 343.275 278.992 320.807L270.587 302.38C255.949 270.289 230.214 244.565 198.118 229.939L180.164 221.758C157.282 211.331 131.078 212.666 105.933 212.666C47.4278 212.666 4.92992e-05 165.059 0 106.333C0 47.607 47.4278 0 105.933 0Z" fill="white"/>
<circle cx="100.426" cy="399.575" r="100.426" fill="white"/>
<path d="M500 100.426C500 155.889 455.037 200.851 399.574 200.851C344.11 200.851 299.148 155.889 299.148 100.426C299.148 44.962 344.11 0 399.574 0C455.037 0 500 44.962 500 100.426Z" fill="white"/>
</svg></svg></g></g></svg></g></svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" fill="none"><rect width="100" height="100" fill="#CBB2FF" rx="12"/><g fill="#09090B" clip-path="url(#a)"><path d="M50.004 41.547a8.446 8.446 0 0 0-8.45 8.449 8.446 8.446 0 0 0 8.45 8.449 8.447 8.447 0 0 0 8.449-8.45 8.446 8.446 0 0 0-8.45-8.448m.042-16.531a2.513 2.513 0 0 0 2.463-2.553A2.486 2.486 0 0 0 49.955 20a2.486 2.486 0 0 0-2.462 2.553 2.486 2.486 0 0 0 2.552 2.463m-.089 49.963a2.486 2.486 0 0 0-2.463 2.552 2.486 2.486 0 0 0 2.552 2.463 2.513 2.513 0 0 0 2.463-2.552 2.486 2.486 0 0 0-2.553-2.463M32.372 32.3a2.513 2.513 0 0 0-.065-3.547 2.487 2.487 0 0 0-3.548.065 2.487 2.487 0 0 0 .065 3.548 2.487 2.487 0 0 0 3.548-.065m35.262 35.394a2.487 2.487 0 0 0 .065 3.548 2.487 2.487 0 0 0 3.548-.065 2.513 2.513 0 0 0-.065-3.548 2.487 2.487 0 0 0-3.548.065m.065-35.329c.98.944 2.572.946 3.548-.065a2.487 2.487 0 0 0-.065-3.548 2.487 2.487 0 0 0-3.548.065 2.487 2.487 0 0 0 .065 3.548M32.307 67.63a2.487 2.487 0 0 0-3.548.065 2.487 2.487 0 0 0 .065 3.548c.98.944 2.572.946 3.548-.065a2.487 2.487 0 0 0-.065-3.548m45.046-20.136a2.487 2.487 0 0 0-2.364 2.647 2.514 2.514 0 0 0 2.646 2.364 2.487 2.487 0 0 0 2.364-2.646 2.484 2.484 0 0 0-2.646-2.362zM22.65 52.501a2.487 2.487 0 0 0 2.365-2.646 2.485 2.485 0 0 0-2.646-2.362 2.487 2.487 0 0 0-2.365 2.646 2.515 2.515 0 0 0 2.646 2.364zm23.463-16.915a4.37 4.37 0 0 0 6.053 1.152c1.125-.765 1.819-2.005 1.904-3.403.062-.908.261-2.196 1.268-2.89 1.03-.655 2.293-.335 3.154-.04 1.32.467 2.732.313 3.875-.425a4.32 4.32 0 0 0 1.894-2.743 4.32 4.32 0 0 0-.597-3.28l-.174-.26a4.37 4.37 0 0 0-6.053-1.152c-1.126.764-1.82 2.003-1.904 3.402-.063.908-.262 2.196-1.269 2.89-1.029.655-2.293.335-3.154.04-1.317-.467-2.731-.313-3.874.425a4.36 4.36 0 0 0-1.3 6.022l.175.262zm-9.602 40.709a4.37 4.37 0 0 0 6.053 1.152c1.126-.765 1.82-2.005 1.904-3.403.063-.908.262-2.197 1.269-2.89 1.029-.655 2.292-.335 3.154-.04 1.32.467 2.731.313 3.874-.425a4.32 4.32 0 0 0 1.895-2.743 4.32 4.32 0 0 0-.597-3.28l-.175-.26a4.37 4.37 0 0 0-6.053-1.152c-1.125.764-1.819 2.003-1.904 3.401-.062.909-.261 2.197-1.268 2.89-1.03.656-2.293.336-3.154.041-1.318-.468-2.732-.313-3.875.425a4.36 4.36 0 0 0-1.3 6.022l.175.262zM57.42 37.048q-.08.434-.076.872a4.37 4.37 0 0 0 3.548 4.217c1.338.252 2.705-.137 3.75-1.067.686-.6 1.735-1.372 2.939-1.15 1.19.261 1.86 1.38 2.261 2.199.604 1.261 1.714 2.15 3.045 2.434a4.33 4.33 0 0 0 3.28-.604 4.32 4.32 0 0 0 1.892-2.745l.062-.309q.082-.435.076-.872a4.37 4.37 0 0 0-3.548-4.217c-1.337-.253-2.702.137-3.749 1.067-.687.6-1.738 1.371-2.94 1.15-1.19-.262-1.86-1.38-2.26-2.2-.605-1.26-1.714-2.149-3.043-2.433a4.36 4.36 0 0 0-5.172 3.349l-.062.309zm-35.539 22.05q-.08.435-.076.873a4.37 4.37 0 0 0 3.548 4.216c1.338.253 2.704-.136 3.75-1.067.686-.6 1.735-1.37 2.938-1.15 1.19.262 1.862 1.38 2.262 2.2.604 1.261 1.714 2.15 3.045 2.433A4.33 4.33 0 0 0 40.627 66a4.32 4.32 0 0 0 1.892-2.744l.063-.31q.081-.433.076-.871a4.37 4.37 0 0 0-3.548-4.217c-1.337-.253-2.702.136-3.749 1.067-.686.6-1.738 1.371-2.94 1.15-1.19-.262-1.86-1.38-2.26-2.2-.605-1.261-1.714-2.149-3.043-2.433a4.36 4.36 0 0 0-5.172 3.349l-.062.308zm42.525-13.01a4.5 4.5 0 0 0-.671.563 4.37 4.37 0 0 0-.472 5.49c.767 1.125 2.009 1.816 3.404 1.897.909.06 2.197.26 2.893 1.264.657 1.027.34 2.29.045 3.154-.466 1.32-.31 2.731.431 3.874a4.33 4.33 0 0 0 2.745 1.89 4.33 4.33 0 0 0 3.28-.601l.261-.175q.365-.25.671-.563a4.37 4.37 0 0 0 .472-5.49c-.767-1.123-2.006-1.816-3.404-1.897-.909-.06-2.2-.26-2.893-1.264-.657-1.026-.34-2.29-.044-3.154.465-1.32.308-2.731-.432-3.872a4.36 4.36 0 0 0-6.024-1.288l-.262.174zM23.687 36.55a4.5 4.5 0 0 0-.671.564 4.37 4.37 0 0 0-.472 5.49c.767 1.125 2.009 1.816 3.405 1.897.908.06 2.196.26 2.892 1.264.658 1.026.34 2.29.045 3.154-.465 1.32-.309 2.731.432 3.874a4.32 4.32 0 0 0 2.744 1.89 4.33 4.33 0 0 0 3.28-.601l.261-.175q.365-.25.672-.563a4.37 4.37 0 0 0 .472-5.49c-.768-1.123-2.007-1.816-3.405-1.897-.908-.06-2.2-.26-2.892-1.264-.658-1.027-.34-2.29-.045-3.154.465-1.32.308-2.731-.432-3.872a4.36 4.36 0 0 0-6.024-1.289l-.262.175zm39.268 20.868a4.4 4.4 0 0 0-.873-.076 4.37 4.37 0 0 0-4.217 3.548c-.252 1.338.137 2.705 1.067 3.75.6.686 1.372 1.735 1.15 2.939-.261 1.19-1.38 1.86-2.199 2.261-1.261.604-2.15 1.714-2.433 3.045a4.33 4.33 0 0 0 .603 3.28 4.32 4.32 0 0 0 2.745 1.892l.309.062q.435.082.872.076a4.37 4.37 0 0 0 4.217-3.547c.253-1.338-.136-2.703-1.067-3.75-.6-.686-1.371-1.738-1.15-2.94.262-1.19 1.38-1.86 2.2-2.26 1.26-.605 2.149-1.714 2.433-3.043a4.36 4.36 0 0 0-3.349-5.172l-.308-.062zM40.9 21.88a4.4 4.4 0 0 0-.872-.077 4.37 4.37 0 0 0-4.217 3.548c-.253 1.338.136 2.704 1.067 3.75.6.686 1.371 1.735 1.15 2.939-.262 1.19-1.38 1.86-2.2 2.261-1.26.604-2.149 1.714-2.433 3.045a4.33 4.33 0 0 0 .604 3.28 4.32 4.32 0 0 0 2.745 1.892l.308.062q.435.081.873.076a4.37 4.37 0 0 0 4.216-3.548c.253-1.337-.136-2.702-1.067-3.749-.6-.687-1.37-1.738-1.15-2.94.262-1.19 1.38-1.86 2.2-2.26 1.261-.605 2.15-1.714 2.433-3.043a4.36 4.36 0 0 0-3.348-5.172l-.31-.062z"/></g><defs><clipPath id="a"><path fill="#fLine truncated

After

Width:  |  Height:  |  Size: 4.9 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#FFFFFF"><path d="M17.392 0H13.9L17 4.808 10.444 0H6.949l3.102 6.3L3.494 0H0l3.05 8.131L0 24h3.494L10.05 6.985 6.949 24h3.494L17 5.494 13.899 24h3.493L24 3.672 17.392 0z"/></svg>

After

Width:  |  Height:  |  Size: 245 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#050038"><path d="M17.392 0H13.9L17 4.808 10.444 0H6.949l3.102 6.3L3.494 0H0l3.05 8.131L0 24h3.494L10.05 6.985 6.949 24h3.494L17 5.494 13.899 24h3.493L24 3.672 17.392 0z"/></svg>

After

Width:  |  Height:  |  Size: 245 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#7856FF"><path d="M6.967 9.996h3.053c-.763-.477-1.048-1.145-1.431-2.384L7.443 3.366C6.919 1.458 6.49.551 4.39.551H.004v1.145h.621c1.286 0 1.431.477 1.814 1.908L3.44 7.326c.524 1.814 1.337 2.67 3.53 2.67h-.003Zm7.06 0h3.053c2.194 0 2.956-.86 3.484-2.67l1.001-3.722c.382-1.431.57-1.908 1.814-1.908H24V.551h-4.34c-2.146 0-2.576.86-3.053 2.815l-1.145 4.246c-.384 1.286-.673 1.907-1.435 2.384Zm-4.007 4.008h4.007V9.996H10.02v4.008ZM0 23.449h4.39c2.1 0 2.529-.907 3.053-2.815l1.146-4.246c.383-1.239.668-1.907 1.431-2.384H6.967c-2.194 0-3.007.86-3.531 2.67l-1.001 3.722c-.383 1.431-.524 1.907-1.814 1.907H0v1.146Zm19.65 0h4.343v-1.146h-.622c-1.239 0-1.431-.476-1.814-1.907l-1.001-3.722c-.524-1.814-1.286-2.67-3.483-2.67h-3.046c.762.477 1.041 1.098 1.424 2.384l1.145 4.246c.477 1.955.907 2.815 3.054 2.815Z"/></svg>

After

Width:  |  Height:  |  Size: 874 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#00C7B7"><path d="M6.49 19.04h-.23L5.13 17.9v-.23l1.73-1.71h1.2l.15.15v1.2L6.5 19.04ZM5.13 6.31V6.1l1.13-1.13h.23L8.2 6.68v1.2l-.15.15h-1.2L5.13 6.31Zm9.96 9.09h-1.65l-.14-.13v-3.83c0-.68-.27-1.2-1.1-1.23-.42 0-.9 0-1.43.02l-.07.08v4.96l-.14.14H8.9l-.13-.14V8.73l.13-.14h3.7a2.6 2.6 0 0 1 2.61 2.6v4.08l-.13.14Zm-8.37-2.44H.14L0 12.82v-1.64l.14-.14h6.58l.14.14v1.64l-.14.14Zm17.14 0h-6.58l-.14-.14v-1.64l.14-.14h6.58l.14.14v1.64l-.14.14ZM11.05 6.55V1.64l.14-.14h1.65l.14.14v4.9l-.14.14h-1.65l-.14-.13Zm0 15.81v-4.9l.14-.14h1.65l.14.13v4.91l-.14.14h-1.65l-.14-.14Z"/></svg>

After

Width:  |  Height:  |  Size: 639 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#FFFFFF"><path d="M4.459 4.208c.746.606 1.026.56 2.428.466l13.215-.793c.28 0 .047-.28-.046-.326L17.86 1.968c-.42-.326-.981-.7-2.055-.607L3.01 2.295c-.466.046-.56.28-.374.466zm.793 3.08v13.904c0 .747.373 1.027 1.214.98l14.523-.84c.841-.046.935-.56.935-1.167V6.354c0-.606-.233-.933-.748-.887l-15.177.887c-.56.047-.747.327-.747.933zm14.337.745c.093.42 0 .84-.42.888l-.7.14v10.264c-.608.327-1.168.514-1.635.514-.748 0-.935-.234-1.495-.933l-4.577-7.186v6.952L12.21 19s0 .84-1.168.84l-3.222.186c-.093-.186 0-.653.327-.746l.84-.233V9.854L7.822 9.76c-.094-.42.14-1.026.793-1.073l3.456-.233 4.764 7.279v-6.44l-1.215-.139c-.093-.514.28-.887.747-.933zM1.936 1.035l13.31-.98c1.634-.14 2.055-.047 3.082.7l4.249 2.986c.7.513.934.653.934 1.213v16.378c0 1.026-.373 1.634-1.68 1.726l-15.458.934c-.98.047-1.448-.093-1.962-.747l-3.129-4.06c-.56-.747-.793-1.306-.793-1.96V2.667c0-.839.374-1.54 1.447-1.632z"/></svg>

After

Width:  |  Height:  |  Size: 962 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#000000"><path d="M4.459 4.208c.746.606 1.026.56 2.428.466l13.215-.793c.28 0 .047-.28-.046-.326L17.86 1.968c-.42-.326-.981-.7-2.055-.607L3.01 2.295c-.466.046-.56.28-.374.466zm.793 3.08v13.904c0 .747.373 1.027 1.214.98l14.523-.84c.841-.046.935-.56.935-1.167V6.354c0-.606-.233-.933-.748-.887l-15.177.887c-.56.047-.747.327-.747.933zm14.337.745c.093.42 0 .84-.42.888l-.7.14v10.264c-.608.327-1.168.514-1.635.514-.748 0-.935-.234-1.495-.933l-4.577-7.186v6.952L12.21 19s0 .84-1.168.84l-3.222.186c-.093-.186 0-.653.327-.746l.84-.233V9.854L7.822 9.76c-.094-.42.14-1.026.793-1.073l3.456-.233 4.764 7.279v-6.44l-1.215-.139c-.093-.514.28-.887.747-.933zM1.936 1.035l13.31-.98c1.634-.14 2.055-.047 3.082.7l4.249 2.986c.7.513.934.653.934 1.213v16.378c0 1.026-.373 1.634-1.68 1.726l-15.458.934c-.98.047-1.448-.093-1.962-.747l-3.129-4.06c-.56-.747-.793-1.306-.793-1.96V2.667c0-.839.374-1.54 1.447-1.632z"/></svg>

After

Width:  |  Height:  |  Size: 962 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#64748B" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="5" y="10" width="14" height="10" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3m-4 4v2"/></svg>

After

Width:  |  Height:  |  Size: 254 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#D3002D"><path d="M12.07 10.679v-.672H9.32v3.97h2.751v-.656H9.975v-1h2.002v-.657H9.975v-.985zm2.049 3.299v-3.971h.656v3.314h1.908v.657zm3.111 0v-3.971h.657v3.314h1.907v.657zm-4.456-3.971h.657v3.97h-.657zM1.079 11.023c-.25.25-.407.594-.407.985 0 .39.157.735.407 1a1.418 1.418 0 0 0 1.986 0c.25-.265.406-.61.406-1 0-.391-.156-.735-.407-.985a1.418 1.418 0 0 0-1.985 0M0 12.008c0-1.142.922-2.064 2.064-2.064 1.141 0 2.064.922 2.064 2.064a2.062 2.062 0 0 1-2.064 2.064A2.062 2.062 0 0 1 0 12.008Zm6.176-.344v-.985h1.329a.5.5 0 0 1 .5.485c0 .281-.234.5-.5.5zm1.657.61c.485-.141.829-.579.829-1.11 0-.642-.516-1.157-1.157-1.157H5.519v3.97h.657v-1.656h.922l.985 1.657h.782zM5.05 10.429a.477.477 0 0 1-.485.484.468.468 0 0 1-.484-.484c0-.266.203-.485.484-.485.266 0 .485.219.485.485zm17.684-.422h-.798l-1.016 1.47-1.016-1.47h-.813l1.485 2.157v1.814h.672v-1.814zm.781.813a.404.404 0 0 1-.406-.407c0-.219.172-.406.406-.406.22 0 .407.187.407.406a.414.414 0 0 1-.407.407m0-.892a.487.487 0 0 0-.484.485c0 .266.219.485.484.485a.487.487 0 0 0 .485-.485.487.487 0 0 0-.485-.485zm-.125.313h.203c.032 0 .063.031.063.063a.067.067 0 0 1-.063.062h-.203zm.344.063a.14.14 0 0 0-.14-.141h-.282v.5h.078v-.219h.14l.11.22h.094l-.11-.22a.15.15 0 0 0 .11-.14"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#06AC38"><path d="M16.965 1.18C15.085.164 13.769 0 10.683 0H3.73v14.55h6.926c2.743 0 4.8-.164 6.61-1.37 1.975-1.303 3.004-3.484 3.004-6.007 0-2.716-1.262-4.896-3.305-5.994zm-5.5 10.326h-4.21V3.113l3.977-.027c3.62-.028 5.43 1.234 5.43 4.128 0 3.113-2.248 4.292-5.197 4.292zM3.73 17.61h3.525V24H3.73Z"/></svg>

After

Width:  |  Height:  |  Size: 374 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#FFFFFF"><path d="M0 12C0 5.373 5.373 0 12 0c4.873 0 9.067 2.904 10.947 7.077l-15.87 15.87a11.981 11.981 0 0 1-1.935-1.099L14.99 12H12l-8.485 8.485A11.962 11.962 0 0 1 0 12Zm12.004 12L24 12.004C23.998 18.628 18.628 23.998 12.004 24Z"/></svg>

After

Width:  |  Height:  |  Size: 308 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#000000"><path d="M0 12C0 5.373 5.373 0 12 0c4.873 0 9.067 2.904 10.947 7.077l-15.87 15.87a11.981 11.981 0 0 1-1.935-1.099L14.99 12H12l-8.485 8.485A11.962 11.962 0 0 1 0 12Zm12.004 12L24 12.004C23.998 18.628 18.628 23.998 12.004 24Z"/></svg>

After

Width:  |  Height:  |  Size: 308 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#FFFFFF"><path d="M9.854 14.5 5 9.647.854 5.5A.5.5 0 0 0 0 5.854V8.44a.5.5 0 0 0 .146.353L5 13.647l.147.146L9.854 18.5l.146.147v-.049c.065.03.134.049.207.049h2.586a.5.5 0 0 0 .353-.854L9.854 14.5zm0-5-4-4a.487.487 0 0 0-.409-.144.515.515 0 0 0-.356.21.493.493 0 0 0-.089.288V8.44a.5.5 0 0 0 .147.353l9 9a.5.5 0 0 0 .853-.354v-2.585a.5.5 0 0 0-.146-.354l-5-5zm1-4a.5.5 0 0 0-.854.354V8.44a.5.5 0 0 0 .147.353l4 4a.5.5 0 0 0 .853-.354V9.854a.5.5 0 0 0-.146-.354l-4-4zm12.647 11.515a3.863 3.863 0 0 1-2.232-1.1l-4.708-4.707a.5.5 0 0 0-.854.354v6.585a.5.5 0 0 0 .5.5H23.5a.5.5 0 0 0 .5-.5v-.6c0-.276-.225-.497-.499-.532zm-5.394.032a.8.8 0 1 1 0-1.6.8.8 0 0 1 0 1.6zM.854 15.5a.5.5 0 0 0-.854.354v2.293a.5.5 0 0 0 .5.5h2.293c.222 0 .39-.135.462-.309a.493.493 0 0 0-.109-.545L.854 15.501zM5 14.647.854 10.5a.5.5 0 0 0-.854.353v2.586a.5.5 0 0 0 .146.353L4.854 18.5l.146.147h2.793a.5.5 0 0 0 .353-.854L5 14.647z"/></svg>

After

Width:  |  Height:  |  Size: 979 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#000000"><path d="M9.854 14.5 5 9.647.854 5.5A.5.5 0 0 0 0 5.854V8.44a.5.5 0 0 0 .146.353L5 13.647l.147.146L9.854 18.5l.146.147v-.049c.065.03.134.049.207.049h2.586a.5.5 0 0 0 .353-.854L9.854 14.5zm0-5-4-4a.487.487 0 0 0-.409-.144.515.515 0 0 0-.356.21.493.493 0 0 0-.089.288V8.44a.5.5 0 0 0 .147.353l9 9a.5.5 0 0 0 .853-.354v-2.585a.5.5 0 0 0-.146-.354l-5-5zm1-4a.5.5 0 0 0-.854.354V8.44a.5.5 0 0 0 .147.353l4 4a.5.5 0 0 0 .853-.354V9.854a.5.5 0 0 0-.146-.354l-4-4zm12.647 11.515a3.863 3.863 0 0 1-2.232-1.1l-4.708-4.707a.5.5 0 0 0-.854.354v6.585a.5.5 0 0 0 .5.5H23.5a.5.5 0 0 0 .5-.5v-.6c0-.276-.225-.497-.499-.532zm-5.394.032a.8.8 0 1 1 0-1.6.8.8 0 0 1 0 1.6zM.854 15.5a.5.5 0 0 0-.854.354v2.293a.5.5 0 0 0 .5.5h2.293c.222 0 .39-.135.462-.309a.493.493 0 0 0-.109-.545L.854 15.501zM5 14.647.854 10.5a.5.5 0 0 0-.854.353v2.586a.5.5 0 0 0 .146.353L4.854 18.5l.146.147h2.793a.5.5 0 0 0 .353-.854L5 14.647z"/></svg>

After

Width:  |  Height:  |  Size: 979 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#FF6C37"><path d="M13.527.099C6.955-.744.942 3.9.099 10.473c-.843 6.572 3.8 12.584 10.373 13.428 6.573.843 12.587-3.801 13.428-10.374C24.744 6.955 20.101.943 13.527.099zm2.471 7.485a.855.855 0 0 0-.593.25l-4.453 4.453-.307-.307-.643-.643c4.389-4.376 5.18-4.418 5.996-3.753zm-4.863 4.861l4.44-4.44a.62.62 0 1 1 .847.903l-4.699 4.125-.588-.588zm.33.694l-1.1.238a.06.06 0 0 1-.067-.032.06.06 0 0 1 .01-.073l.645-.645.512.512zm-2.803-.459l1.172-1.172.879.878-1.979.426a.074.074 0 0 1-.085-.039.072.072 0 0 1 .013-.093zm-3.646 6.058a.076.076 0 0 1-.069-.083.077.077 0 0 1 .022-.046h.002l.946-.946 1.222 1.222-2.123-.147zm2.425-1.256a.228.228 0 0 0-.117.256l.203.865a.125.125 0 0 1-.211.117h-.003l-.934-.934-.294-.295 3.762-3.758 1.82-.393.874.874c-1.255 1.102-2.971 2.201-5.1 3.268zm5.279-3.428h-.002l-.839-.839 4.699-4.125a.952.952 0 0 0 .119-.127c-.148 1.345-2.029 3.245-3.977 5.091zm3.657-6.46l-.003-.002a1.822 1.822 0 0 1 2.459-2.684l-1.61 1.613a.119.119 0 0 0 0 .169l1.247 1.247a1.817 1.817 0 0 1-2.093-.343zm2.578 0a1.714 1.714 0 0 1-.271.218h-.001l-1.207-1.207 1.533-1.533c.661.72.637 1.832-.054 2.522zM18.855 6.05a.143.143 0 0 0-.053.157.416.416 0 0 1-.053.45.14.14 0 0 0 .023.197.141.141 0 0 0 .084.03.14.14 0 0 0 .106-.05.691.691 0 0 0 .087-.751.138.138 0 0 0-.194-.033z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#FFFFFF"><path d="M22.436 0l-11.91 7.773-1.174 4.276 6.625-4.297L11.65 24h4.391l6.395-24zM14.26 10.098L3.389 17.166 1.564 24h9.008l3.688-13.902Z"/></svg>

After

Width:  |  Height:  |  Size: 220 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#0C2451"><path d="M22.436 0l-11.91 7.773-1.174 4.276 6.625-4.297L11.65 24h4.391l6.395-24zM14.26 10.098L3.389 17.166 1.564 24h9.008l3.688-13.902Z"/></svg>

After

Width:  |  Height:  |  Size: 220 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#FFFFFF"><path d="M14.679 0c4.648 0 7.413 2.765 7.413 6.434s-2.765 6.434-7.413 6.434H12.33L24 24h-8.245l-8.88-8.44c-.636-.588-.93-1.273-.93-1.86 0-.831.587-1.565 1.713-1.883l4.574-1.224c1.737-.465 2.936-1.81 2.936-3.572 0-2.153-1.761-3.4-3.939-3.4H0V0z"/></svg>

After

Width:  |  Height:  |  Size: 328 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#000000"><path d="M14.679 0c4.648 0 7.413 2.765 7.413 6.434s-2.765 6.434-7.413 6.434H12.33L24 24h-8.245l-8.88-8.44c-.636-.588-.93-1.273-.93-1.86 0-.831.587-1.565 1.713-1.883l4.574-1.224c1.737-.465 2.936-1.81 2.936-3.572 0-2.153-1.761-3.4-3.939-3.4H0V0z"/></svg>

After

Width:  |  Height:  |  Size: 328 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#FFFFFF"><path d="m23.327 15.205-.893-1.555-4.321 2.632 4.799-6.11.726-.426-.179-.27.33-.421-1.515-1.261-.693.883-13.992 8.186 5.173-6.221 9.636-5.282-.915-1.769-5.248 2.876 2.584-3.106-1.481-1.305-5.816 6.994-5.777 3.168 4.423-5.847 2.771-1.442-.88-1.789-8.075 4.203L6.186 4.43 4.648 3.198 0 9.349l.072.058.868 1.768 5.153-2.683-4.696 6.207.77.617.458.885 5.425-2.974-5.974 7.185 1.481 1.304.297-.358 14.411-8.459-4.785 6.094.078.065-.007.005.992 1.726 6.364-3.877-2.451 3.954 1.642 1.077L24 15.648z"/></svg>

After

Width:  |  Height:  |  Size: 576 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#0D0E12"><path d="m23.327 15.205-.893-1.555-4.321 2.632 4.799-6.11.726-.426-.179-.27.33-.421-1.515-1.261-.693.883-13.992 8.186 5.173-6.221 9.636-5.282-.915-1.769-5.248 2.876 2.584-3.106-1.481-1.305-5.816 6.994-5.777 3.168 4.423-5.847 2.771-1.442-.88-1.789-8.075 4.203L6.186 4.43 4.648 3.198 0 9.349l.072.058.868 1.768 5.153-2.683-4.696 6.207.77.617.458.885 5.425-2.974-5.974 7.185 1.481 1.304.297-.358 14.411-8.459-4.785 6.094.078.065-.007.005.992 1.726 6.364-3.877-2.451 3.954 1.642 1.077L24 15.648z"/></svg>

After

Width:  |  Height:  |  Size: 576 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#FFFFFF"><path d="M13.91 2.505c-.873-1.448-2.972-1.448-3.844 0L6.904 7.92a15.478 15.478 0 0 1 8.53 12.811h-2.221A13.301 13.301 0 0 0 5.784 9.814l-2.926 5.06a7.65 7.65 0 0 1 4.435 5.848H2.194a.365.365 0 0 1-.298-.534l1.413-2.402a5.16 5.16 0 0 0-1.614-.913L.296 19.275a2.182 2.182 0 0 0 .812 2.999 2.24 2.24 0 0 0 1.086.288h6.983a9.322 9.322 0 0 0-3.845-8.318l1.11-1.922a11.47 11.47 0 0 1 4.95 10.24h5.915a17.242 17.242 0 0 0-7.885-15.28l2.244-3.845a.37.37 0 0 1 .504-.13c.255.14 9.75 16.708 9.928 16.9a.365.365 0 0 1-.327.543h-2.287c.029.612.029 1.223 0 1.831h2.297a2.206 2.206 0 0 0 1.922-3.31z"/></svg>

After

Width:  |  Height:  |  Size: 670 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#362D59"><path d="M13.91 2.505c-.873-1.448-2.972-1.448-3.844 0L6.904 7.92a15.478 15.478 0 0 1 8.53 12.811h-2.221A13.301 13.301 0 0 0 5.784 9.814l-2.926 5.06a7.65 7.65 0 0 1 4.435 5.848H2.194a.365.365 0 0 1-.298-.534l1.413-2.402a5.16 5.16 0 0 0-1.614-.913L.296 19.275a2.182 2.182 0 0 0 .812 2.999 2.24 2.24 0 0 0 1.086.288h6.983a9.322 9.322 0 0 0-3.845-8.318l1.11-1.922a11.47 11.47 0 0 1 4.95 10.24h5.915a17.242 17.242 0 0 0-7.885-15.28l2.244-3.845a.37.37 0 0 1 .504-.13c.255.14 9.75 16.708 9.928 16.9a.365.365 0 0 1-.327.543h-2.287c.029.612.029 1.223 0 1.831h2.297a2.206 2.206 0 0 0 1.922-3.31z"/></svg>

After

Width:  |  Height:  |  Size: 670 B

+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 109.5 124.5">
<path fill="#95BF47" d="M95.9 23.9c-.1-.6-.6-1-1.1-1-.5 0-9.3-.2-9.3-.2s-7.4-7.2-8.1-7.9c-.7-.7-2.2-.5-2.7-.3 0 0-1.4.4-3.7 1.1-.4-1.3-1-2.8-1.8-4.4-2.6-5-6.5-7.7-11.1-7.7h0c-.3 0-.6 0-1 .1-.1-.2-.3-.3-.4-.5-2-2.2-4.6-3.2-7.7-3.1-6 .2-12 4.5-16.8 12.2-3.4 5.4-6 12.2-6.8 17.5-6.9 2.1-11.7 3.6-11.8 3.7-3.5 1.1-3.6 1.2-4 4.5-.3 2.5-9.5 73-9.5 73l76.4 13.2 33.1-8.2S96 24.5 95.9 23.9ZM67.2 16.8c-1.8.5-3.8 1.2-5.9 1.8 0-3-.4-7.3-1.8-10.9C64 8.6 66.2 13.7 67.2 16.8Zm-10 3.1c-4 1.2-8.4 2.6-12.8 3.9 1.2-4.7 3.6-9.4 6.4-12.5 1.1-1.1 2.6-2.4 4.3-3.2 1.8 3.5 2.2 8.4 2.1 11.8ZM49.1 4c1.4 0 2.6.3 3.6.9C51.1 5.8 49.5 7 48 8.6c-3.8 4.1-6.7 10.5-7.9 16.6-3.6 1.1-7.2 2.2-10.5 3.2C31.7 18.8 39.8 4.3 49.1 4Z"/>
<path fill="#5E8E3E" d="M94.8 22.9c-.5 0-9.3-.2-9.3-.2s-7.4-7.2-8.1-7.9c-.3-.3-.6-.4-1-.5V124l33.1-8.2S96 24.5 95.9 23.8c-.1-.5-.6-.9-1.1-.9Z"/>
<path fill="#FFF" d="m58 39.9-3.8 14.4s-4.3-2-9.4-1.6c-7.5.5-7.5 5.2-7.5 6.4.4 6.4 17.3 7.8 18.3 22.9.7 11.9-6.3 20-16.4 20.6-12.2.8-18.9-6.4-18.9-6.4l2.6-11s6.7 5.1 12.1 4.7c3.5-.2 4.8-3.1 4.7-5.1-.5-8.4-14.3-7.9-15.2-21.7-.7-11.6 6.9-23.4 23.7-24.4 6.5-.5 9.8 1.2 9.8 1.2Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#FFFFFF"><path d="M22.099 5.781c-1.283-2-3.14-3.67-5.27-4.52l-.63-.213a7.433 7.433 0 0 0-2.15-.331c-2.307.01-4.175 1.92-4.175 4.275a4.3 4.3 0 0 0 .867 2.602l-.26-.342c.124.186.26.37.417.556.663.802 1.604 1.635 2.822 2.58 2.999 2.32 4.943 4.378 5.104 6.93.038.344.062.696.062 1.051 0 1.297-.283 2.67-.764 3.635h.005s-.207.377-.077.487c.066.057.21.1.46-.053a12.104 12.104 0 0 0 3.4-3.33 12.111 12.111 0 0 0 2.088-6.635 12.098 12.098 0 0 0-1.9-6.692zm-9.096 8.718-1.878-1.55c-3.934-2.87-5.98-5.966-4.859-9.783a8.73 8.73 0 0 1 .37-1.016v-.004s.278-.583-.327-.295a12.067 12.067 0 0 0-6.292 9.975 12.11 12.11 0 0 0 2.053 7.421 9.394 9.394 0 0 0 2.154 2.168H4.22c4.148 3.053 7.706 1.446 7.706 1.446h.003a4.847 4.847 0 0 0 2.962-4.492 4.855 4.855 0 0 0-1.889-3.87z"/></svg>

After

Width:  |  Height:  |  Size: 832 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#092540"><path d="M22.099 5.781c-1.283-2-3.14-3.67-5.27-4.52l-.63-.213a7.433 7.433 0 0 0-2.15-.331c-2.307.01-4.175 1.92-4.175 4.275a4.3 4.3 0 0 0 .867 2.602l-.26-.342c.124.186.26.37.417.556.663.802 1.604 1.635 2.822 2.58 2.999 2.32 4.943 4.378 5.104 6.93.038.344.062.696.062 1.051 0 1.297-.283 2.67-.764 3.635h.005s-.207.377-.077.487c.066.057.21.1.46-.053a12.104 12.104 0 0 0 3.4-3.33 12.111 12.111 0 0 0 2.088-6.635 12.098 12.098 0 0 0-1.9-6.692zm-9.096 8.718-1.878-1.55c-3.934-2.87-5.98-5.966-4.859-9.783a8.73 8.73 0 0 1 .37-1.016v-.004s.278-.583-.327-.295a12.067 12.067 0 0 0-6.292 9.975 12.11 12.11 0 0 0 2.053 7.421 9.394 9.394 0 0 0 2.154 2.168H4.22c4.148 3.053 7.706 1.446 7.706 1.446h.003a4.847 4.847 0 0 0 2.962-4.492 4.855 4.855 0 0 0-1.889-3.87z"/></svg>

After

Width:  |  Height:  |  Size: 832 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#635BFF"><path d="M13.976 9.15c-2.172-.806-3.356-1.426-3.356-2.409 0-.831.683-1.305 1.901-1.305 2.227 0 4.515.858 6.09 1.631l.89-5.494C18.252.975 15.697 0 12.165 0 9.667 0 7.589.654 6.104 1.872 4.56 3.147 3.757 4.992 3.757 7.218c0 4.039 2.467 5.76 6.476 7.219 2.585.92 3.445 1.574 3.445 2.583 0 .98-.84 1.545-2.354 1.545-1.875 0-4.965-.921-6.99-2.109l-.9 5.555C5.175 22.99 8.385 24 11.714 24c2.641 0 4.843-.624 6.328-1.813 1.664-1.305 2.525-3.236 2.525-5.732 0-4.128-2.524-5.851-6.594-7.305h.003z"/></svg>

After

Width:  |  Height:  |  Size: 572 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#3FCF8E"><path d="M11.9 1.036c-.015-.986-1.26-1.41-1.874-.637L.764 12.05C-.33 13.427.65 15.455 2.409 15.455h9.579l.113 7.51c.014.985 1.259 1.408 1.873.636l9.262-11.653c1.093-1.375.113-3.403-1.645-3.403h-9.642z"/></svg>

After

Width:  |  Height:  |  Size: 285 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#FFFFFF"><path d="M21.328 19.239 15.283.631a.913.913 0 0 0-1.15-.586l-3.374 1.096a2.146 2.146 0 1 1-4.083 1.325L3.131 3.62a.73.73 0 0 0-.469.92l6.159 18.955a.73.73 0 0 0 .92.469l3.545-1.152a2.148 2.148 0 1 1 4.085-1.327l3.372-1.096a.913.913 0 0 0 .586-1.15zM10.302 14.52H8.578V8.345H6.506V6.673h5.857v1.672h-2.061zm6.865-3.577h-2.061v6.176H13.38v-6.176h-2.073V9.271h5.86z"/></svg>

After

Width:  |  Height:  |  Size: 447 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#222432"><path d="M21.328 19.239 15.283.631a.913.913 0 0 0-1.15-.586l-3.374 1.096a2.146 2.146 0 1 1-4.083 1.325L3.131 3.62a.73.73 0 0 0-.469.92l6.159 18.955a.73.73 0 0 0 .92.469l3.545-1.152a2.148 2.148 0 1 1 4.085-1.327l3.372-1.096a.913.913 0 0 0 .586-1.15zM10.302 14.52H8.578V8.345H6.506V6.673h5.857v1.672h-2.061zm6.865-3.577h-2.061v6.176H13.38v-6.176h-2.073V9.271h5.86z"/></svg>

After

Width:  |  Height:  |  Size: 447 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#4772FA"><path d="M12 0C5.383 0 0 5.383 0 12s5.383 12 12 12 12-5.383 12-12h-2.7c0 5.128-4.172 9.3-9.3 9.3-5.128 0-9.3-4.172-9.3-9.3 0-5.128 4.172-9.3 9.3-9.3V0Zm7.4 2.583-7.505 9.371L8.388 9.08l-2.002 2.436 4.741 3.888a1.573 1.573 0 0 0 2.231-.233l8.504-10.617L19.4 2.583Z"/></svg>

After

Width:  |  Height:  |  Size: 348 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#E44332"><path d="M21 0H3C1.35 0 0 1.35 0 3v3.858s3.854 2.24 4.098 2.38c.31.18.694.177 1.004 0 .26-.147 8.02-4.608 8.136-4.675.279-.161.58-.107.748-.01.164.097.606.348.84.48.232.134.221.502.013.622l-9.712 5.59c-.346.2-.69.204-1.048.002C3.478 10.907.998 9.463 0 8.882v2.02l4.098 2.38c.31.18.694.177 1.004 0 .26-.147 8.02-4.609 8.136-4.676.279-.16.58-.106.748-.008.164.096.606.347.84.48.232.133.221.5.013.62-.208.121-9.288 5.346-9.712 5.59-.346.2-.69.205-1.048.002C3.478 14.951.998 13.506 0 12.926v2.02l4.098 2.38c.31.18.694.177 1.004 0 .26-.147 8.02-4.609 8.136-4.676.279-.16.58-.106.748-.009.164.097.606.348.84.48.232.133.221.502.013.622l-9.712 5.59c-.346.199-.69.204-1.048.001C3.478 18.994.998 17.55 0 16.97V21c0 1.65 1.35 3 3 3h18c1.65 0 3-1.35 3-3V3c0-1.65-1.35-3-3-3z"/></svg>

After

Width:  |  Height:  |  Size: 847 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#FFFFFF"><path d="m12 1.608 12 20.784H0Z"/></svg>

After

Width:  |  Height:  |  Size: 116 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#000000"><path d="m12 1.608 12 20.784H0Z"/></svg>

After

Width:  |  Height:  |  Size: 116 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#146EF5"><path d="m24 4.515-7.658 14.97H9.149l3.205-6.204h-.144C9.566 16.713 5.621 18.973 0 19.485v-6.118s3.596-.213 5.71-2.435H0V4.515h6.417v5.278l.144-.001 2.622-5.277h4.854v5.244h.144l2.72-5.244H24Z"/></svg>

After

Width:  |  Height:  |  Size: 277 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#0C6EFC"><path d="m0 7.354 2.113 9.292h.801a1.54 1.54 0 0 0 1.506-1.218l1.351-6.34a.171.171 0 0 1 .167-.137c.08 0 .15.058.167.137l1.352 6.34a1.54 1.54 0 0 0 1.506 1.218h.805l2.113-9.292h-.565c-.62 0-1.159.43-1.296 1.035l-1.26 5.545-1.106-5.176a1.76 1.76 0 0 0-2.19-1.324c-.639.176-1.113.716-1.251 1.365l-1.094 5.127-1.26-5.537A1.33 1.33 0 0 0 .563 7.354H0zm13.992 0a.951.951 0 0 0-.951.95v8.342h.635a.952.952 0 0 0 .951-.95V7.353h-.635zm1.778 0 3.158 4.66-3.14 4.632h1.325c.368 0 .712-.181.918-.486l1.756-2.59a.12.12 0 0 1 .197 0l1.754 2.59c.206.305.55.486.918.486h1.326l-3.14-4.632L24 7.354h-1.326c-.368 0-.712.181-.918.486l-1.772 2.617a.12.12 0 0 1-.197 0L18.014 7.84a1.108 1.108 0 0 0-.918-.486H15.77z"/></svg>

After

Width:  |  Height:  |  Size: 780 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#13B5EA"><path d="M12 0C5.373 0 0 5.373 0 12s5.373 12 12 12 12-5.373 12-12S18.627 0 12 0zm6.585 14.655c-1.485 0-2.69-1.206-2.69-2.689 0-1.485 1.207-2.691 2.69-2.691 1.485 0 2.69 1.207 2.69 2.691s-1.207 2.689-2.69 2.689zM7.53 14.644c-.099 0-.192-.041-.267-.116l-2.043-2.04-2.052 2.047c-.069.068-.16.108-.258.108-.202 0-.368-.166-.368-.368 0-.099.04-.191.111-.263l2.04-2.05-2.038-2.047c-.075-.069-.113-.162-.113-.261 0-.203.166-.366.368-.366.098 0 .188.037.258.105l2.055 2.048 2.048-2.045c.069-.071.162-.108.26-.108.211 0 .375.165.375.366 0 .098-.029.188-.104.258l-2.056 2.055 2.055 2.051c.068.069.104.16.104.258 0 .202-.165.368-.365.368h-.01zm8.017-4.591c-.796.101-.882.476-.882 1.404v2.787c0 .202-.165.366-.366.366-.203 0-.367-.165-.368-.366v-4.53c0-.204.16-.366.362-.366.166 0 .316.125.346.289.27-.209.6-.317.93-.317h.105c.195 0 .359.165.359.368 0 .201-.164.352-.375.359 0 0-.09 0-.164.008l.053-.002zm-3.091 2.205H8.625c0 .019.003.037.006.057.02.105.045.211.083.31.194.531.765 1.275 1.829 1.29.33-.003.631-.086.9-.229.21-.12.391-.271.525-.428.045-.058.09-.112.12-.168.18-.229.405-.186.54-.083.164.135.18.391.045.57l-.016.016c-.21.27-.435.495-.689.66-.255.164-.525.284-.811.345-.33.09-.645.104-.975.06-1.095-.135-2.01-.93-2.28-2.01-.06-.21-.09-.42-.09-.645 0-.855.421-1.695 1.125-2.205.885-.615 2.085-.66 3-.075.63.405 1.035 1.021 1.185 1.771.075.419-.21.794-.734.81l.068-.046zm6.129-2.223c-1.064 0-1.931.865-1.931 1.931 0 1.064.866 1.931 1.931 1.931s1.931-.867 1.931-1.931c0-1.065-.866-1.933-1.931-1.933v.002zm0 2.595c-.367 0-.666-.297-.666-.666 0-.367.3-.665.666-.665.367 0 .667.299.667.665 0 .369-.3.667-.667.666zm-8.04-2.603c-.91 0-1.672.623-1.886 1.466v.03h3.776c-.203-.855-.973-1.494-1.891-1.494v-.002z"/></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#FF4F00"><path d="M4.157 0A4.151 4.151 0 0 0 0 4.161v15.678A4.151 4.151 0 0 0 4.157 24h15.682A4.152 4.152 0 0 0 24 19.839V4.161A4.152 4.152 0 0 0 19.839 0H4.157Zm10.61 8.761h.03a.577.577 0 0 1 .23.038.585.585 0 0 1 .201.124.63.63 0 0 1 .162.431.612.612 0 0 1-.162.435.58.58 0 0 1-.201.128.58.58 0 0 1-.23.042.529.529 0 0 1-.235-.042.585.585 0 0 1-.332-.328.559.559 0 0 1-.038-.235.613.613 0 0 1 .17-.431.59.59 0 0 1 .405-.162Zm2.853 1.572c.03.004.061.004.095.004.325-.011.646.064.937.219.238.144.431.355.552.609.128.279.189.582.185.888v.193a2 2 0 0 1 0 .219h-2.498c.003.227.075.45.204.642a.78.78 0 0 0 .646.265.714.714 0 0 0 .484-.136.642.642 0 0 0 .23-.318l.915.257a1.398 1.398 0 0 1-.28.537c-.14.159-.321.284-.521.355a2.234 2.234 0 0 1-.836.136 1.923 1.923 0 0 1-1.001-.245 1.618 1.618 0 0 1-.665-.703 2.221 2.221 0 0 1-.227-1.036 1.95 1.95 0 0 1 .48-1.398 1.9 1.9 0 0 1 1.3-.488Zm-9.607.023c.162.004.325.026.48.079.207.065.4.174.563.314.26.302.393.692.366 1.088v2.276H8.53l-.109-.711h-.065c-.064.163-.155.31-.272.439a1.122 1.122 0 0 1-.374.264 1.023 1.023 0 0 1-.453.083 1.334 1.334 0 0 1-.866-.264.965.965 0 0 1-.329-.801.993.993 0 0 1 .076-.431 1.02 1.02 0 0 1 .242-.363 1.478 1.478 0 0 1 1.043-.303h.952v-.181a.696.696 0 0 0-.136-.454.553.553 0 0 0-.438-.154.695.695 0 0 0-.378.086.48.48 0 0 0-.193.254l-.99-.144a1.26 1.26 0 0 1 .257-.563c.14-.174.321-.302.533-.378.261-.091.54-.136.82-.129.053-.003.106-.007.163-.007Zm4.384.007c.174 0 .347.038.506.114.182.083.34.211.458.374.257.423.377.911.351 1.406a2.53 2.53 0 0 1-.355 1.448 1.148 1.148 0 0 1-1.009.517c-.204 0-.401-.045-.582-.136a1.052 1.052 0 0 1-.48-.457 1.298 1.298 0 0 1-.114-.234h-.045l.004 1.784h-1.059v-4.713h.904l.117.805h.057c.068-.208.177-.401.328-.56a1.129 1.129 0 0 1 .843-.344h.076v-.004Zm7.559.084h.903l.113.805h.053a1.37 1.37 0 0 1 .235-.484.813.813 0 0 1 .313-.242.82.82 0 0 1 .39-.076h.234v1.051h-.401a.662.662 0 0 0-.313.008.623.623 0 0 0-.272.155.663.663 0 0 0-.174.26.683.683 0 0 0-.027.314v1.875h-1.054v-3.666Zm-17.515.003h3.262v.896L3.73 13.104l.034.113h1.973l.042.9H2.4v-.9l1.931-1.754-.045-.117H2.441v-.896Zm11.815 0h1.055v3.659h-1.055V10.45Zm3.443.684.019.016a.69.69 0 0 0-.351.045.756.756 0 0 0-.287.204c-.11.155-.174.336-.189.522h1.545c-.034-.526-.257-.787-.74-.787h.003Zm-5.718.163c-.026 0-.057 0-.083.004a.78.78 0 0 0-.31.053.746.746 0 0 0-.257.189 1.016 1.016 0 0 0-.204.695v.064c-.015.257.057.507.204.711a.634.634 0 0 0 .253.196.638.638 0 0 0 .314.061.644.644 0 0 0 .578-.265c.14-.223.204-.48.189-.74a1.216 1.216 0 0 0-.181-.711.677.677 0 0 0-.503-.257Zm-4.509 1.266a.464.464 0 0 0-.268.102.373.373 0 0 0-.114.276c0 .053.008.106.027.155a.375.375 0 0 0 .087.132.576.576 0 0 0 .397.11v.004a.863.863 0 0 0 .563-.182.573.573 0 0 0 .211-.457v-.14h-.903Z"/></svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

@@ -51,6 +51,27 @@ vi.mock("@/api/tools", () => ({
toolsApi: mockToolsApi,
}));
vi.mock("@/pages/apps/AppLogo", () => ({
AppLogo: ({
name,
brandKey,
logoUrl,
allowRemoteFallback,
}: {
name: string;
brandKey?: string | null;
logoUrl?: string | null;
allowRemoteFallback?: boolean;
}) => (
<span
data-app-logo={name}
data-brand-key={brandKey ?? ""}
data-logo-url={logoUrl ?? ""}
data-allow-remote-fallback={allowRemoteFallback ? "true" : "false"}
/>
),
}));
vi.mock("./SidebarNavItem", () => ({
SidebarNavItem: (props: {
to: string;
@@ -93,6 +114,7 @@ async function flushReact() {
function connection(overrides: Record<string, unknown> = {}) {
return {
id: "conn-1",
applicationId: "app-1",
name: "GitHub",
transport: "mcp_remote",
status: "active",
@@ -185,6 +207,63 @@ describe("AppConnectionSidebar", () => {
expect(container.querySelector('[data-to="/apps/conn-1/setup"]')?.getAttribute("data-active")).toBe("false");
});
it("uses the application key for a customized connection display name", async () => {
mockToolsApi.getConnection.mockResolvedValue(connection({ name: "Dotta's GitHub" }));
await renderSidebar();
expect(container.querySelector("[data-app-logo]")?.getAttribute("data-brand-key")).toBe("github");
expect(container.querySelector("[data-app-logo]")?.getAttribute("data-allow-remote-fallback")).toBe("true");
});
it("keeps remote fallback disabled until the stable application key resolves", async () => {
let resolveApplications!: (value: {
applications: Array<{ id: string; applicationKey: string; name: string; status: string }>;
}) => void;
mockToolsApi.getConnection.mockResolvedValue(connection({ name: "Dotta's source control" }));
mockToolsApi.listApplications.mockReturnValueOnce(new Promise((resolve) => {
resolveApplications = resolve;
}));
await renderSidebar();
expect(container.querySelector("[data-app-logo]")?.getAttribute("data-brand-key")).toBe("");
expect(container.querySelector("[data-app-logo]")?.getAttribute("data-allow-remote-fallback")).toBe("false");
await act(async () => {
resolveApplications({
applications: [{ id: "app-1", applicationKey: "github", name: "GitHub", status: "active" }],
});
});
await flushReact();
expect(container.querySelector("[data-app-logo]")?.getAttribute("data-brand-key")).toBe("github");
expect(container.querySelector("[data-app-logo]")?.getAttribute("data-allow-remote-fallback")).toBe("true");
});
it("allows remote fallback after application identity lookup fails", async () => {
mockToolsApi.listApplications.mockRejectedValueOnce(new Error("Application lookup unavailable"));
await renderSidebar();
expect(container.querySelector("[data-app-logo]")?.getAttribute("data-allow-remote-fallback")).toBe("true");
});
it("keeps a customized connection logo when application identity lookup fails", async () => {
mockToolsApi.getConnection.mockResolvedValue(connection({
name: "Dotta's source control",
config: { sourceTemplateKey: "github" },
}));
mockToolsApi.listApplications.mockRejectedValueOnce(new Error("Application lookup unavailable"));
await renderSidebar();
const logo = container.querySelector("[data-app-logo]");
expect(logo?.getAttribute("data-brand-key")).toBe("github");
expect(logo?.getAttribute("data-logo-url")).toBe("https://example.test/github.png");
expect(logo?.getAttribute("data-allow-remote-fallback")).toBe("true");
});
it("renders application-mode tabs under the not-connected app route", async () => {
currentPath.value = "/apps/app/app-1/review";
+18 -7
View File
@@ -18,6 +18,8 @@ import {
import { isComposioBrokerConnection } from "@/pages/apps/composio-services";
import { AppLogo } from "@/pages/apps/AppLogo";
import {
appApplicationSourceSlug,
appConnectionSourceSlug,
appDefinitionLogoUrl,
appDefinitionName,
appDefinitionSlug,
@@ -41,7 +43,7 @@ export function AppDetailSidebar(props: AppDetailSidebarProps) {
const applicationsQuery = useQuery({
queryKey: queryKeys.tools.applications(selectedCompanyId ?? "__none__"),
queryFn: () => toolsApi.listApplications(selectedCompanyId!),
enabled: props.kind === "application" && !!selectedCompanyId,
enabled: !!selectedCompanyId,
});
const connectionsQuery = useQuery({
queryKey: queryKeys.tools.connections(selectedCompanyId ?? "__none__"),
@@ -62,9 +64,8 @@ export function AppDetailSidebar(props: AppDetailSidebarProps) {
const connection = connectionQuery.data;
const isBroker = isComposioBrokerConnection(connection);
const application = props.kind === "application"
? (applicationsQuery.data?.applications ?? []).find((app) => app.id === props.applicationId)
: null;
const applicationId = props.kind === "application" ? props.applicationId : connection?.applicationId;
const application = (applicationsQuery.data?.applications ?? []).find((app) => app.id === applicationId) ?? null;
const appConnections = props.kind === "application"
? (connectionsQuery.data?.connections ?? []).filter((candidate) => candidate.applicationId === props.applicationId)
: [];
@@ -75,6 +76,9 @@ export function AppDetailSidebar(props: AppDetailSidebarProps) {
connection,
application ?? undefined,
);
const brandKey = appApplicationSourceSlug(application)
?? appConnectionSourceSlug(connection)
?? (logoEntry ? appDefinitionSlug(logoEntry) : null);
const reviewConnectionId = connection?.id ?? previousConnection?.id ?? null;
const attentionItem = reviewConnectionId
? attentionQuery.data?.apps.find((app) => app.connection.id === reviewConnectionId)
@@ -97,7 +101,13 @@ export function AppDetailSidebar(props: AppDetailSidebarProps) {
<span className="truncate">All apps</span>
</Link>
<div className="flex min-w-0 items-center gap-2 px-2 py-1">
<AppLogo name={appName} logoUrl={appDefinitionLogoUrl(logoEntry)} size={28} />
<AppLogo
name={appName}
brandKey={brandKey}
logoUrl={appDefinitionLogoUrl(logoEntry)}
allowRemoteFallback={!applicationsQuery.isPending}
size={28}
/>
<span className="flex-1 truncate text-sm font-bold text-foreground">{appName}</span>
</div>
</div>
@@ -142,8 +152,9 @@ function galleryEntryFor(
connection: ToolConnection | undefined,
application: ToolApplication | undefined,
): AppGalleryDisplayEntry | null {
if (application?.applicationKey) {
const keyed = apps.find((app) => appDefinitionSlug(app) === application.applicationKey);
const sourceSlug = appApplicationSourceSlug(application) ?? appConnectionSourceSlug(connection);
if (sourceSlug) {
const keyed = apps.find((app) => appDefinitionSlug(app) === sourceSlug);
if (keyed) return keyed;
}
const name = (connection?.name ?? application?.name)?.toLowerCase();
+52
View File
@@ -0,0 +1,52 @@
import { existsSync, readFileSync, statSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
type BrandProvider = {
slug: string;
localAsset: string;
darkAsset?: string;
assetType: "svg" | "png";
darkVariantRequired: boolean;
};
type BrandManifest = {
schemaVersion: number;
providers: BrandProvider[];
};
const uiRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
const manifestPath = path.join(uiRoot, "public", "brands", "apps", "manifest.json");
const manifest = JSON.parse(readFileSync(manifestPath, "utf8")) as BrandManifest;
function publicAssetPath(asset: string): string {
expect(asset).toMatch(/^\/brands\/apps\/[a-z0-9-]+\.(svg|png)$/);
return path.join(uiRoot, "public", asset.slice(1));
}
describe("local app brand assets", () => {
it("maps every provider to a unique local asset that exists", () => {
expect(manifest.schemaVersion).toBe(1);
expect(manifest.providers.length).toBeGreaterThan(50);
expect(new Set(manifest.providers.map((provider) => provider.slug)).size).toBe(
manifest.providers.length,
);
for (const provider of manifest.providers) {
const assetPath = publicAssetPath(provider.localAsset);
expect(existsSync(assetPath), `${provider.slug} local asset should exist`).toBe(true);
expect(statSync(assetPath).isFile(), `${provider.slug} local asset should be a file`).toBe(true);
expect(path.extname(assetPath)).toBe(`.${provider.assetType}`);
}
});
it("ships each required dark-theme variant", () => {
for (const provider of manifest.providers.filter((entry) => entry.darkVariantRequired)) {
expect(provider.darkAsset, `${provider.slug} should declare a dark asset`).toBeTruthy();
const assetPath = publicAssetPath(provider.darkAsset!);
expect(existsSync(assetPath), `${provider.slug} dark asset should exist`).toBe(true);
expect(statSync(assetPath).isFile(), `${provider.slug} dark asset should be a file`).toBe(true);
}
});
});
+74
View File
@@ -0,0 +1,74 @@
export type AppBrandManifestProvider = {
slug: string;
provider: string;
localAsset: string;
darkAsset?: string;
};
export type AppBrandManifest = {
schemaVersion: number;
providers: AppBrandManifestProvider[];
};
export type LocalAppBrandAssets = {
light: string;
dark?: string;
};
let manifestPromise: Promise<AppBrandManifest> | null = null;
function normalizedProviderKey(value: string): string {
return value
.trim()
.toLowerCase()
.replace(/&/g, "and")
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-+|-+$/g, "");
}
function isManifest(value: unknown): value is AppBrandManifest {
if (!value || typeof value !== "object") return false;
const candidate = value as Partial<AppBrandManifest>;
return candidate.schemaVersion === 1 && Array.isArray(candidate.providers);
}
async function loadManifest(): Promise<AppBrandManifest> {
if (!manifestPromise) {
manifestPromise = fetch("/brands/apps/manifest.json", { credentials: "same-origin" })
.then(async (response) => {
if (!response.ok) throw new Error(`App brand manifest request failed (${response.status})`);
const value: unknown = await response.json();
if (!isManifest(value)) throw new Error("App brand manifest is invalid");
return value;
})
.catch((error) => {
manifestPromise = null;
throw error;
});
}
return manifestPromise;
}
export function resolveLocalAppBrandAssets(
manifest: AppBrandManifest,
providerName: string,
): LocalAppBrandAssets | null {
const key = normalizedProviderKey(providerName);
if (!key) return null;
const provider = manifest.providers.find((candidate) =>
candidate.slug === key || normalizedProviderKey(candidate.provider) === key,
);
if (!provider || typeof provider.localAsset !== "string" || !provider.localAsset) return null;
return {
light: provider.localAsset,
...(typeof provider.darkAsset === "string" && provider.darkAsset ? { dark: provider.darkAsset } : {}),
};
}
export async function loadLocalAppBrandAssets(providerName: string): Promise<LocalAppBrandAssets | null> {
return resolveLocalAppBrandAssets(await loadManifest(), providerName);
}
export function resetAppBrandManifestCacheForTests() {
manifestPromise = null;
}
+52
View File
@@ -10,6 +10,7 @@ import { APP_TABS } from "./app-tabs";
const getConnectionMock = vi.hoisted(() => vi.fn());
const getConnectionInstallsMock = vi.hoisted(() => vi.fn());
const listApplicationsMock = vi.hoisted(() => vi.fn());
const listGalleryMock = vi.hoisted(() => vi.fn());
const listConnectionsMock = vi.hoisted(() => vi.fn());
const listCatalogMock = vi.hoisted(() => vi.fn());
@@ -40,6 +41,7 @@ vi.mock("@/api/tools", () => ({
toolsApi: {
getConnection: (connectionId: string) => getConnectionMock(connectionId),
getConnectionInstalls: (connectionId: string) => getConnectionInstallsMock(connectionId),
listApplications: (companyId: string) => listApplicationsMock(companyId),
listGallery: (companyId: string) => listGalleryMock(companyId),
listConnections: (companyId: string) => listConnectionsMock(companyId),
listCatalog: (connectionId: string) => listCatalogMock(connectionId),
@@ -76,6 +78,27 @@ vi.mock("@/api/tools", () => ({
},
}));
vi.mock("./AppLogo", () => ({
AppLogo: ({
name,
brandKey,
logoUrl,
allowRemoteFallback,
}: {
name: string;
brandKey?: string | null;
logoUrl?: string | null;
allowRemoteFallback?: boolean;
}) => (
<span
data-app-logo={name}
data-brand-key={brandKey ?? ""}
data-logo-url={logoUrl ?? ""}
data-allow-remote-fallback={allowRemoteFallback ? "true" : "false"}
/>
),
}));
vi.mock("@/api/access", () => ({
accessApi: {
listUserDirectory: (companyId: string) => listUserDirectoryMock(companyId),
@@ -267,6 +290,9 @@ describe("AppDetail", () => {
mockSearchParams.value = new URLSearchParams();
getConnectionMock.mockResolvedValue(connection());
getConnectionInstallsMock.mockResolvedValue({ connectionId: "conn-1", installs: [] });
listApplicationsMock.mockResolvedValue({
applications: [{ id: "app-1", applicationKey: "github", name: "GitHub", status: "active" }],
});
listConnectionsMock.mockResolvedValue({ connections: [] });
listConnectionGrantsMock.mockResolvedValue({
connection: { id: "conn-1", uid: "conn-1" },
@@ -408,6 +434,32 @@ describe("AppDetail", () => {
expect(updateConnectionMock).toHaveBeenCalledWith("conn-1", { enabled: false });
});
it("allows the gallery logo fallback after application identity lookup fails", async () => {
listApplicationsMock.mockRejectedValueOnce(new Error("Application lookup unavailable"));
await renderAppDetail();
expect(container.querySelector("[data-app-logo]")?.getAttribute("data-allow-remote-fallback")).toBe("true");
});
it("keeps a customized connection logo after application identity lookup fails", async () => {
getConnectionMock.mockResolvedValue(connection({
name: "Dotta's source control",
config: {
url: "https://github.example/mcp",
sourceTemplateKey: "github",
},
}));
listApplicationsMock.mockRejectedValueOnce(new Error("Application lookup unavailable"));
await renderAppDetail();
const logo = container.querySelector("[data-app-logo]");
expect(logo?.getAttribute("data-brand-key")).toBe("github");
expect(logo?.getAttribute("data-logo-url")).toBe("https://example.com/github.png");
expect(logo?.getAttribute("data-allow-remote-fallback")).toBe("true");
});
it("keeps the unverified-server marker on URL-only connection details", async () => {
getConnectionMock.mockResolvedValue(
connection({
+35 -3
View File
@@ -2,6 +2,7 @@ import { useEffect, useMemo, useRef, useState } from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { Check, Loader2, Pencil } from "lucide-react";
import type {
ToolApplication,
ToolConnection,
ToolPolicy,
ToolProfileWithDetails,
@@ -31,6 +32,8 @@ import { cn } from "@/lib/utils";
import { AppLogo } from "./AppLogo";
import { UnverifiedServerBadge } from "./UnverifiedServerBadge";
import {
appApplicationSourceSlug,
appConnectionSourceSlug,
appDefinitionLogoUrl,
appDefinitionName,
appDefinitionSlug,
@@ -85,6 +88,11 @@ export function AppDetail() {
queryFn: () => toolsApi.listConnections(selectedCompanyId!),
enabled: !!selectedCompanyId && activeTab === "setup",
});
const applicationsQuery = useQuery({
queryKey: queryKeys.tools.applications(selectedCompanyId ?? "__none__"),
queryFn: () => toolsApi.listApplications(selectedCompanyId!),
enabled: !!selectedCompanyId && !!activeTab,
});
const installsQuery = useQuery({
queryKey: queryKeys.tools.connectionInstalls(connectionId),
queryFn: () => toolsApi.getConnectionInstalls(connectionId),
@@ -143,15 +151,21 @@ export function AppDetail() {
});
const connection = connectionQuery.data;
const application = connection
? (applicationsQuery.data?.applications ?? []).find((candidate) => candidate.id === connection.applicationId)
: undefined;
const composioChildConnectionCount = (connectionsQuery.data?.connections ?? []).filter(
(candidate) => candidate.status !== "archived"
&& candidate.config?.provider === "composio"
&& candidate.config?.parentConnectionId === connectionId,
).length;
const logoEntry = useMemo(
() => galleryEntryFor((galleryQuery.data?.apps ?? []) as AppGalleryDisplayEntry[], connection),
[galleryQuery.data, connection],
() => galleryEntryFor((galleryQuery.data?.apps ?? []) as AppGalleryDisplayEntry[], connection, application),
[galleryQuery.data, connection, application],
);
const brandKey = appApplicationSourceSlug(application)
?? appConnectionSourceSlug(connection)
?? (logoEntry ? appDefinitionSlug(logoEntry) : null);
const userProfileById = useMemo(
() => buildCompanyUserProfileMap(userDirectoryQuery.data?.users),
[userDirectoryQuery.data],
@@ -579,6 +593,8 @@ export function AppDetail() {
appName={appName}
connection={connection}
logoEntry={logoEntry}
brandKey={brandKey}
allowRemoteLogo={!applicationsQuery.isPending}
status={status}
actionCount={actionCount}
owner={owner}
@@ -750,6 +766,8 @@ function AppDetailHeader({
appName,
connection,
logoEntry,
brandKey,
allowRemoteLogo,
status,
actionCount,
owner,
@@ -764,6 +782,8 @@ function AppDetailHeader({
appName: string;
connection: ToolConnection;
logoEntry: AppGalleryDisplayEntry | null;
brandKey: string | null;
allowRemoteLogo: boolean;
status: StatusInfo;
actionCount: number | null;
owner: ConnectionOwnerProfile | null;
@@ -781,7 +801,13 @@ function AppDetailHeader({
return (
<header className="flex flex-wrap items-start justify-between gap-4">
<div className="flex items-center gap-3">
<AppLogo name={appName} logoUrl={appDefinitionLogoUrl(logoEntry)} size={44} />
<AppLogo
name={appName}
brandKey={brandKey}
logoUrl={appDefinitionLogoUrl(logoEntry)}
allowRemoteFallback={allowRemoteLogo}
size={44}
/>
<div>
{renaming ? (
<form
@@ -977,8 +1003,14 @@ function accessFrom(
function galleryEntryFor(
apps: AppGalleryDisplayEntry[],
connection: ToolConnection | undefined,
application: ToolApplication | undefined,
): AppGalleryDisplayEntry | null {
if (!connection) return null;
const sourceSlug = appApplicationSourceSlug(application) ?? appConnectionSourceSlug(connection);
if (sourceSlug) {
const keyed = apps.find((app) => appDefinitionSlug(app) === sourceSlug);
if (keyed) return keyed;
}
const name = connection.name.toLowerCase();
return apps.find((app) => appDefinitionName(app).toLowerCase() === name) ??
apps.find((app) => appDefinitionSlug(app) === name) ??
@@ -0,0 +1,227 @@
// @vitest-environment jsdom
import { act } from "react";
import { createRoot } from "react-dom/client";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { resetAppBrandManifestCacheForTests } from "@/lib/app-brand-assets";
import { AppLogo } from "./AppLogo";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true;
describe("AppLogo local brand assets", () => {
let container: HTMLDivElement;
let root: ReturnType<typeof createRoot>;
beforeEach(() => {
container = document.createElement("div");
document.body.appendChild(container);
resetAppBrandManifestCacheForTests();
});
afterEach(() => {
act(() => root?.unmount());
container.remove();
vi.unstubAllGlobals();
});
it("loads the manifest and renders its light and dark provider assets", async () => {
const fetchMock = vi.fn().mockResolvedValue({
ok: true,
json: async () => ({
schemaVersion: 1,
providers: [{
slug: "github",
provider: "GitHub",
localAsset: "/brands/apps/github.svg",
darkAsset: "/brands/apps/github-dark.svg",
}],
}),
});
vi.stubGlobal("fetch", fetchMock);
root = createRoot(container);
await act(async () => {
root.render(<AppLogo name="GitHub" logoUrl="https://remote.example/github.svg" />);
await Promise.resolve();
await Promise.resolve();
});
expect(fetchMock).toHaveBeenCalledWith("/brands/apps/manifest.json", { credentials: "same-origin" });
const images = Array.from(container.querySelectorAll("img"));
expect(images.map((image) => image.getAttribute("src"))).toEqual([
"/brands/apps/github.svg",
"/brands/apps/github-dark.svg",
]);
expect(images[0]?.className).toContain("dark:hidden");
expect(images[1]?.className).toContain("dark:block");
});
it.each([
{ failedIndex: 0, survivingAsset: "/brands/apps/github-dark.svg", survivingClass: "dark:block", fallbackClass: "dark:hidden" },
{ failedIndex: 1, survivingAsset: "/brands/apps/github.svg", survivingClass: "dark:hidden", fallbackClass: "dark:flex" },
])("keeps the valid theme asset when image $failedIndex fails", async ({
failedIndex,
survivingAsset,
survivingClass,
fallbackClass,
}) => {
vi.stubGlobal("fetch", vi.fn().mockResolvedValue({
ok: true,
json: async () => ({
schemaVersion: 1,
providers: [{
slug: "github",
provider: "GitHub",
localAsset: "/brands/apps/github.svg",
darkAsset: "/brands/apps/github-dark.svg",
}],
}),
}));
root = createRoot(container);
await act(async () => {
root.render(<AppLogo name="GitHub" />);
await Promise.resolve();
await Promise.resolve();
});
const failedImage = container.querySelectorAll("img")[failedIndex];
expect(failedImage).toBeTruthy();
await act(async () => {
failedImage!.dispatchEvent(new Event("error", { bubbles: true }));
});
const remainingImages = Array.from(container.querySelectorAll("img"));
expect(remainingImages).toHaveLength(1);
expect(remainingImages[0]?.getAttribute("src")).toBe(survivingAsset);
expect(remainingImages[0]?.className).toContain(survivingClass);
const fallback = container.querySelector("span > span");
expect(fallback?.textContent).toBe("G");
expect(fallback?.className).toContain(fallbackClass);
});
it("does not request a remote logo while the local manifest lookup is pending", async () => {
let resolveResponse!: (value: {
ok: boolean;
json: () => Promise<unknown>;
}) => void;
vi.stubGlobal("fetch", vi.fn().mockImplementation(() => new Promise((resolve) => {
resolveResponse = resolve;
})));
root = createRoot(container);
act(() => {
root.render(<AppLogo name="GitHub" logoUrl="https://remote.example/github.svg" />);
});
expect(container.querySelector("img")).toBeNull();
await act(async () => {
resolveResponse({
ok: true,
json: async () => ({
schemaVersion: 1,
providers: [{
slug: "github",
provider: "GitHub",
localAsset: "/brands/apps/github.svg",
}],
}),
});
await Promise.resolve();
await Promise.resolve();
});
expect(container.querySelector("img")?.getAttribute("src")).toBe("/brands/apps/github.svg");
});
it("uses a stable provider key for owner-qualified display names", async () => {
vi.stubGlobal("fetch", vi.fn().mockResolvedValue({
ok: true,
json: async () => ({
schemaVersion: 1,
providers: [{
slug: "github",
provider: "GitHub",
localAsset: "/brands/apps/github.svg",
}],
}),
}));
root = createRoot(container);
await act(async () => {
root.render(
<AppLogo
name="Dotta's GitHub"
brandKey="github"
logoUrl="https://remote.example/github.svg"
/>,
);
await Promise.resolve();
await Promise.resolve();
});
expect(container.querySelector("img")?.getAttribute("src")).toBe("/brands/apps/github.svg");
});
it("does not expose a remote logo before a stable provider key arrives", async () => {
vi.stubGlobal("fetch", vi.fn().mockResolvedValue({
ok: true,
json: async () => ({
schemaVersion: 1,
providers: [{
slug: "github",
provider: "GitHub",
localAsset: "/brands/apps/github.svg",
}],
}),
}));
root = createRoot(container);
await act(async () => {
root.render(
<AppLogo
name="Dotta's source control"
logoUrl="https://remote.example/github.svg"
allowRemoteFallback={false}
/>,
);
await Promise.resolve();
await Promise.resolve();
});
expect(container.querySelector("img")).toBeNull();
await act(async () => {
root.render(
<AppLogo
name="Dotta's source control"
brandKey="github"
logoUrl="https://remote.example/github.svg"
allowRemoteFallback
/>,
);
await Promise.resolve();
await Promise.resolve();
});
expect(container.querySelector("img")?.getAttribute("src")).toBe("/brands/apps/github.svg");
});
it("keeps the caller logo when the manifest has no matching provider", async () => {
vi.stubGlobal("fetch", vi.fn().mockResolvedValue({
ok: true,
json: async () => ({ schemaVersion: 1, providers: [] }),
}));
root = createRoot(container);
await act(async () => {
root.render(<AppLogo name="Custom MCP" logoUrl="https://remote.example/custom.svg" />);
await Promise.resolve();
await Promise.resolve();
});
expect(container.querySelector("img")?.getAttribute("src")).toBe("https://remote.example/custom.svg");
});
});
+113 -12
View File
@@ -1,4 +1,5 @@
import { useState } from "react";
import { useEffect, useState } from "react";
import { loadLocalAppBrandAssets, type LocalAppBrandAssets } from "@/lib/app-brand-assets";
import { cn } from "@/lib/utils";
const TILE_COLORS = [
@@ -20,7 +21,9 @@ function colorFor(seed: string): string {
interface AppLogoProps {
name: string;
brandKey?: string | null;
logoUrl?: string | null;
allowRemoteFallback?: boolean;
size?: number;
className?: string;
}
@@ -30,25 +33,123 @@ interface AppLogoProps {
* favicon when available, falling back to a coloured letter tile (deterministic
* colour per app name) when the image is missing or fails to load.
*/
export function AppLogo({ name, logoUrl, size = 36, className }: AppLogoProps) {
const [failed, setFailed] = useState(false);
export function AppLogo({
name,
brandKey,
logoUrl,
allowRemoteFallback = true,
size = 36,
className,
}: AppLogoProps) {
const [failedLogoUrls, setFailedLogoUrls] = useState<ReadonlySet<string>>(() => new Set());
const lookupKey = brandKey?.trim() || name;
const [localAssetResult, setLocalAssetResult] = useState<{
lookupKey: string;
assets: LocalAppBrandAssets | null;
} | null>(null);
const localLookupComplete = localAssetResult?.lookupKey === lookupKey;
const localAssets = localLookupComplete ? localAssetResult.assets : null;
const letter = (name.trim()[0] ?? "?").toUpperCase();
const dimension = { width: size, height: size };
// Do not expose a remote caller URL until the local manifest has had a
// chance to resolve this provider. Otherwise the browser requests the
// remote asset during the first render even when a bundled mark exists.
const resolvedLogoUrl = localLookupComplete
? localAssets?.light ?? (allowRemoteFallback ? logoUrl : null)
: null;
const lightLogoUrl = resolvedLogoUrl && !failedLogoUrls.has(resolvedLogoUrl)
? resolvedLogoUrl
: null;
const requestedDarkLogoUrl = localAssets?.dark ?? null;
const darkLogoUrl = requestedDarkLogoUrl && !failedLogoUrls.has(requestedDarkLogoUrl)
? requestedDarkLogoUrl
: null;
const hasDistinctThemeLogos = Boolean(
resolvedLogoUrl && requestedDarkLogoUrl && resolvedLogoUrl !== requestedDarkLogoUrl,
);
const fallbackLogoUrl = lightLogoUrl ?? darkLogoUrl;
if (logoUrl && !failed) {
useEffect(() => {
let active = true;
void loadLocalAppBrandAssets(lookupKey)
.then((assets) => {
if (active) setLocalAssetResult({ lookupKey, assets });
})
.catch(() => {
if (active) setLocalAssetResult({ lookupKey, assets: null });
});
return () => {
active = false;
};
}, [lookupKey]);
useEffect(() => {
setFailedLogoUrls(new Set());
}, [resolvedLogoUrl, localAssets?.dark]);
const markLogoFailed = (url: string) => {
setFailedLogoUrls((current) => new Set(current).add(url));
};
if (fallbackLogoUrl) {
return (
<span
className={cn("inline-flex shrink-0 items-center justify-center overflow-hidden rounded-lg bg-muted", className)}
style={dimension}
>
<img
src={logoUrl}
alt=""
width={size}
height={size}
className="h-full w-full object-contain"
onError={() => setFailed(true)}
/>
{hasDistinctThemeLogos ? (
<>
{lightLogoUrl ? (
<img
src={lightLogoUrl}
alt=""
width={size}
height={size}
className="h-full w-full object-contain dark:hidden"
onError={() => markLogoFailed(lightLogoUrl)}
/>
) : (
<span
className={cn(
"flex h-full w-full items-center justify-center font-bold text-white dark:hidden",
colorFor(name),
)}
aria-hidden="true"
>
{letter}
</span>
)}
{darkLogoUrl ? (
<img
src={darkLogoUrl}
alt=""
width={size}
height={size}
className="hidden h-full w-full object-contain dark:block"
onError={() => markLogoFailed(darkLogoUrl)}
/>
) : (
<span
className={cn(
"hidden h-full w-full items-center justify-center font-bold text-white dark:flex",
colorFor(name),
)}
aria-hidden="true"
>
{letter}
</span>
)}
</>
) : (
<img
src={fallbackLogoUrl}
alt=""
width={size}
height={size}
className="h-full w-full object-contain"
onError={() => markLogoFailed(fallbackLogoUrl)}
/>
)}
</span>
);
}
+9 -3
View File
@@ -64,6 +64,7 @@ type AppRow = {
application: ToolApplication;
connection: ToolConnection | null;
displayName: string;
brandKey: string;
owner: ConnectionOwnerProfile | null;
remainingAgentAvailableConnectionCount: number;
status: AppStatus;
@@ -230,9 +231,11 @@ export function Connections() {
const rows = useMemo<AppRow[]>(() => {
return applications.flatMap((application): AppRow[] => {
const appConnections = connectionsByApplication.get(application.id) ?? [];
const galleryEntry = logoByKey.get(appApplicationSourceSlug(application) ?? "");
const logoUrl = appDefinitionLogoUrl(galleryEntry) ??
appDefinitionLogoUrl(logoByName.get(application.name.toLowerCase()));
const appSourceSlug = appApplicationSourceSlug(application);
const galleryEntry = logoByKey.get(appSourceSlug ?? "") ??
logoByName.get(application.name.toLowerCase());
const logoUrl = appDefinitionLogoUrl(galleryEntry);
const brandKey = appSourceSlug ?? application.name;
const agentAvailableConnectionCount = appConnections.filter(
(connection) => connection.status === "active" && connection.enabled,
).length;
@@ -241,6 +244,7 @@ export function Connections() {
application,
connection: null,
displayName: application.name,
brandKey,
owner: null,
remainingAgentAvailableConnectionCount: 0,
status: statusFor(application, []),
@@ -255,6 +259,7 @@ export function Connections() {
application,
connection,
displayName: connectionDisplayNameForOwner(connection, application.name, owner),
brandKey,
owner,
remainingAgentAvailableConnectionCount: Math.max(
0,
@@ -401,6 +406,7 @@ export function Connections() {
<div className="flex items-center gap-3">
<AppLogo
name={row.displayName}
brandKey={row.brandKey}
logoUrl={row.logoUrl}
size={32}
/>
+7 -1
View File
@@ -1,4 +1,4 @@
import type { AppDefinition, ToolApplication } from "@paperclipai/shared";
import type { AppDefinition, ToolApplication, ToolConnection } from "@paperclipai/shared";
export type AppGalleryDisplayEntry = AppDefinition & {
key?: string;
@@ -40,3 +40,9 @@ export function appApplicationSourceSlug(application: ToolApplication | null | u
}
return key;
}
export function appConnectionSourceSlug(connection: ToolConnection | null | undefined): string | null {
if (!connection) return null;
const source = connection.config?.sourceTemplateKey ?? connection.transportConfig?.sourceTemplateKey;
return typeof source === "string" && source.trim() ? source.trim() : null;
}