mirror of
https://github.com/stoatchat/for-web.git
synced 2026-07-13 22:51:48 -04:00
[PR #766] feat: klipy gifbox url #768
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/stoatchat/for-web/pull/766
Author: @dorkian-gray
Created: 2/15/2026
Status: 🔄 Open
Base:
main← Head:feat/klipy-gifbox-url📝 Commits (10+)
c3d44bcfeat(gifbox): read gifbox URL from API config instead of hardcoded api.gifbox.medfdf859feat(gifbox): read gifbox URL and attribution from API config2c5b3e3fix(gifbox): use defensive type casting for gifbox config access49bdae5fix(gifbox): use defensive type casting for gifbox config access03d9288fix(gifbox): fetch gifbox config directly from API root, bypassing SDK type stripping9320764fix(gifbox): use VITE_API_URL env var for gifbox config fetch176d6aefix(gifbox): send gif media URL instead of missing top-level url field5f2a647fix(gifbox): fallback through gif format variants for send URL5fbc1affix(gifbox): use webm format URL which is available from gifbox proxyf8170cffix(gifbox): use itemurl page link for GIF embeds instead of direct media URL📊 Changes
1 file changed (+46 additions, -6 deletions)
View changed files
📝
packages/client/components/ui/components/features/messaging/composition/picker/GifPicker.tsx(+46 -6)📄 Description
Summary
Makes the GIF picker's gifbox URL and search placeholder text configurable via the API root response, and sends GIF media URLs directly for proper embedding. Replaces the hardcoded
api.gifbox.meURL with a dynamic configuration fetched at runtime.Background
The GIF picker previously hardcoded
https://api.gifbox.mein three places withinGifPicker.tsx, forcing all instances - including self-hosted deployments - to use the official gifbox instance. Self-hosters running their own gifbox service (e.g. with Klipy or another GIF provider) had no way to point the client at it.This PR depends on a corresponding backend change that exposes gifbox configuration in the API root response under
features.gifbox, includingurl,enabled, andattributionfields.Changes
packages/client/components/ui/components/features/messaging/composition/picker/GifPicker.tsx:gifboxConfigsignal that fetches the gifbox configuration directly from the API root endpoint (VITE_API_URL) on first usehttps://api.gifbox.meURLs with the configuredgifboxConfig().urlenabled: !!gifboxConfig()to alluseQuerycalls so they wait for the gifbox URL to be available before firinggifboxConfig().attribution, falling back to "Search for GIFs..." when emptyprops.item.media_formats.gif.url(direct GIF media URL) instead ofprops.item.url(which is empty or a page URL depending on the provider)Why fetch from the API root directly?
The client SDK (
stoat.js) imports itsRevoltConfigtype from thestoat-apipackage, which does not yet include thegifboxfield in itsfeaturestype definition. At runtime, the SDK strips unknown fields from API responses when parsing them into typed objects - soclient.configuration.features.gifboxis genuinelyundefined, not just hidden from TypeScript.Rather than forking or patching the SDK types, this PR fetches the gifbox config with a standalone
fetch()call to the API root usingimport.meta.env.VITE_API_URL. The fetch is deduplicated at the module level so it only runs once regardless of how many times the GIF picker is opened.Why send
media_formats.gif.urlinstead ofprops.item.url?The original Tenor flow sent a shareable page URL (e.g.
tenor.com/view/...) as message content. January would fetch the page, extract OG metadata, and create aSpecial::GIFembed. Klipy's page URLs don't have OG metadata (client-side rendered SPA), so this approach doesn't work.Instead, the client sends the direct
.gifURL frommedia_formats.gif. The backend's gifbox proxy has been updated to requestgifformat alongsidewebmandtinywebm, and January has been updated to recognizestatic.klipy.comimage URLs and createSpecial::GIFembeds for them, which the client renders inline without proxying.How it works
gifboxfrom[hosts]andgifbox_attributionfrom the top level ofRevolt.toml/) exposes these asfeatures.gifbox.urlandfeatures.gifbox.attributioncategories,trending,search) use the configured URL instead of a hardcoded onemedia_formats.gif.urlas message contentstatic.klipy.comURL and creates aSpecial::GIFembedSpecial::GIFembeds)For instances that don't set a gifbox host, the backend reports
enabled: falsewith an empty URL, and the GIF picker's queries remain disabled.Related
Testing
Tested against a self-hosted Stoat instance with gifbox proxying to Klipy (
https://api.klipy.com/v2), confirming that GIF categories, trending, search, and sending all work end-to-end. GIFs render inline asSpecial::GIFembeds. Attribution placeholder text confirmed working whengifbox_attributionis set.🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.