@orchyn/mcp 1.12.3 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -9
- package/dist/shared/tools.js +98 -50
- package/dist/shared/ui-template.js +95 -26
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
MCP (Model Context Protocol) server for [orchyn](https://orchyn.com) — fetch,
|
|
4
4
|
discover and understand social posts (TikTok, Instagram, YouTube, X/Twitter,
|
|
5
|
-
Douyin, Xiaohongshu, Bilibili) with your orchyn account and orchyn
|
|
5
|
+
Douyin, Xiaohongshu, Bilibili, LinkedIn) with your orchyn account and orchyn
|
|
6
|
+
credits.
|
|
6
7
|
|
|
7
8
|
## Install (one link)
|
|
8
9
|
|
|
@@ -38,12 +39,12 @@ npx @orchyn/mcp login # one-time sign-in (Google)
|
|
|
38
39
|
|
|
39
40
|
| Tool | Credits | Description |
|
|
40
41
|
|------|---------|-------------|
|
|
41
|
-
| `analyze_post` | first free* | **Preferred.** Analyze any post (video, image, carousel/slideshow) from a TikTok/Instagram/YouTube/X-Twitter/Douyin/Xiaohongshu/Bilibili URL — imports the media and runs AI analysis over the actual content (video frames, carousel images, caption). Returns a `jobId` to poll via `GET /ai/analyze-post`. *First analysis free per workspace via the dashboard free grant.* |
|
|
42
|
+
| `analyze_post` | first free* | **Preferred.** Analyze any post (video, image, carousel/slideshow, text) from a TikTok/Instagram/YouTube/X-Twitter/Douyin/Xiaohongshu/Bilibili/LinkedIn URL — imports the media and runs AI analysis over the actual content (video frames, carousel images, caption). Returns a `jobId` to poll via `GET /ai/analyze-post`. *First analysis free per workspace via the dashboard free grant.* |
|
|
42
43
|
| `get_social_media` | 1 | Fetch a post's media from a URL: `contentType` (video/image/carousel/slideshow), title, caption, author, stats, direct media URLs **+ inline thumbnail image in chat**. |
|
|
43
44
|
| `discover_social_posts` | 2 | **Preferred.** Find recent posts (video/image/carousel/slideshow) for a niche on YouTube, TikTok, Instagram, Douyin, Xiaohongshu, X/Twitter or Bilibili. Each post includes title/caption, views/likes/comments, author, `externalUrl` + **inline thumbnails (4 at a time)** — see *Images in chat* below. Supports `limit`/`offset` pagination (“next”). |
|
|
44
|
-
| `get_user_posts` | 2 | List recent posts by a creator handle (e.g. `@zoundsapp`) on TikTok, Instagram, YouTube, Douyin, Xiaohongshu, X/Twitter or
|
|
45
|
-
| `analyze_creator_profile` | 15 | Deep-dive a whole creator profile on TikTok, Instagram, YouTube, Douyin, Xiaohongshu, X/Twitter or
|
|
46
|
-
| `get_post_comments` | 2 | Fetch top comments for a post URL on TikTok, Instagram, YouTube, Douyin, X/Twitter or
|
|
45
|
+
| `get_user_posts` | 2 | List recent posts by a creator handle (e.g. `@zoundsapp`) on TikTok, Instagram, YouTube, Douyin, Xiaohongshu, X/Twitter, Bilibili or LinkedIn (LinkedIn uses the profile `public_id`, e.g. `billgates`) — title/caption, `thumbnailUrl`, `externalUrl`, views/likes/comments, **inline thumbnails (up to 4)**. Use to scan a whole profile or spot patterns across an account. |
|
|
46
|
+
| `analyze_creator_profile` | 15 | Deep-dive a whole creator profile on TikTok, Instagram, YouTube, Douyin, Xiaohongshu, X/Twitter, Bilibili or LinkedIn: fetches recent posts, runs multimodal AI on up to 3, then synthesizes a profile report — creator summary, niche, content themes, hook styles, strengths/weaknesses, engagement patterns, audience insights, variation ideas, collaboration fit. |
|
|
47
|
+
| `get_post_comments` | 2 | Fetch top comments for a post URL on TikTok, Instagram, YouTube, Douyin, X/Twitter, Bilibili or LinkedIn, plus keyword clusters from TikTok Analytics when available — audience sentiment/audience-signal analysis. |
|
|
47
48
|
| `search_creators` | 2 | Search creators by niche/keyword on TikTok, Instagram, Xiaohongshu, YouTube or Douyin — username, nickname, follower count, signature, verified status. Use to find influencers to vet or analyze. |
|
|
48
49
|
| `get_similar_creators` | 2 | Find lookalike creators for a given handle (TikTok similar-user recommendations or Instagram similar users) — “if this creator works, here are more like them”. |
|
|
49
50
|
| `discover_sounds` | 2 | Discover trending sounds/music for a keyword on TikTok or Instagram — sound choice is a huge ranking signal for TikTok virality. Returns title, artist, duration, play/cover URLs. |
|
|
@@ -57,9 +58,23 @@ All tools require a connected orchyn account and are billed against your orchyn
|
|
|
57
58
|
|
|
58
59
|
`*` `analyze_post` also bills against **workspace** credits (first free grant); the other tools bill against **per-user MCP** credits (never tied to an app/workspace).
|
|
59
60
|
|
|
60
|
-
##
|
|
61
|
+
## Interactive cards in Claude / ChatGPT chat
|
|
61
62
|
|
|
62
|
-
Every tool that returns posts also renders **inline
|
|
63
|
+
Every tool that returns posts also renders **inline interactive cards** directly
|
|
64
|
+
in the chat (MCP Apps `ui://orchyn/view` resource rendered in a sandboxed
|
|
65
|
+
iframe):
|
|
66
|
+
|
|
67
|
+
- **Video posts** (TikTok/IG/YouTube/Douyin/LinkedIn) — an inline `<video>`
|
|
68
|
+
player with the thumbnail as poster, playing the re-hosted permanent MP4
|
|
69
|
+
(no expiring CDN tokens).
|
|
70
|
+
- **Carousels / slideshows** — a horizontally scrollable strip of every slide
|
|
71
|
+
with an image count chip.
|
|
72
|
+
- **Single images** — inline thumbnail.
|
|
73
|
+
- **Text-only posts** (LinkedIn / X) — a styled quote block of the post text.
|
|
74
|
+
- **Official brand marks** — each card shows the platform's real logo
|
|
75
|
+
(simple-icons) in its brand color instead of an emoji.
|
|
76
|
+
|
|
77
|
+
Also **inline thumbnails** render for the model / plain-text clients:
|
|
63
78
|
|
|
64
79
|
- `get_social_media` / `understand_social_post` / `analyze_post` — up to 4 frames inline (poster + carousel slides). The full `mediaItems[].preview_url` + `thumbnailUrl` stay in `structuredContent` for the model to reason over.
|
|
65
80
|
- `discover_social_posts` / `get_user_posts` / `analyze_creator_profile` — each returned post shows its thumbnail inline (up to 4 at once) together with its title/caption + views/likes/comments. Say **"next"** or **"show more"** — Claude will re-call with `offset`/`limit` pagination. Say **"analyze the 2nd one"** — Claude calls `analyze_post` or `understand_social_post` on that URL.
|
|
@@ -256,8 +271,10 @@ per the MCP 2025-03-26 spec):
|
|
|
256
271
|
- Douyin: `douyin.com/*`
|
|
257
272
|
- Xiaohongshu: `xiaohongshu.com/*`, `xhslink.com/*`
|
|
258
273
|
- Bilibili: `bilibili.com/*`, `b23.tv/*`
|
|
274
|
+
- LinkedIn: `linkedin.com/*` (posts, profile URLs)
|
|
259
275
|
|
|
260
|
-
All tools accept these hosts and handle **video, image, carousel
|
|
276
|
+
All tools accept these hosts and handle **video, image, carousel, slideshow
|
|
277
|
+
and text** posts.
|
|
261
278
|
|
|
262
279
|
## Troubleshooting
|
|
263
280
|
|
|
@@ -302,7 +319,7 @@ package for **Claude Desktop**, **Cursor**, and **OpenAI Agents SDK**.
|
|
|
302
319
|
```bash
|
|
303
320
|
npm install
|
|
304
321
|
npm run build # tsc
|
|
305
|
-
npm test # vitest (
|
|
322
|
+
npm test # vitest (66 unit tests, mocked fetch — no network)
|
|
306
323
|
```
|
|
307
324
|
|
|
308
325
|
## License
|
package/dist/shared/tools.js
CHANGED
|
@@ -11,13 +11,31 @@ import { OrchynError } from "./orchyn.js";
|
|
|
11
11
|
import { formatPaywallError, runVideoAnalysis, validatePostUrl } from "./video.js";
|
|
12
12
|
import { ORCHYN_UI_TEMPLATE } from "./ui-template.js";
|
|
13
13
|
/** Current MCP server version — bumped on every deploy for traceability. */
|
|
14
|
-
export const MCP_SERVER_VERSION = "1.
|
|
14
|
+
export const MCP_SERVER_VERSION = "1.13.0";
|
|
15
15
|
/** MCP Apps extension identifier */
|
|
16
16
|
const UI_EXTENSION = "io.modelcontextprotocol/ui";
|
|
17
17
|
/** MIME type for MCP Apps HTML resources */
|
|
18
18
|
const RESOURCE_MIME_TYPE = "text/html;profile=mcp-app";
|
|
19
19
|
/** Single UI resource URI shared by all tools */
|
|
20
20
|
const UI_RESOURCE_URI = "ui://orchyn/view";
|
|
21
|
+
/** Convert an arbitrary http(s) URL to a base64 MCP `image` data block. */
|
|
22
|
+
async function fetchAsBase64Image(url, mimeType) {
|
|
23
|
+
try {
|
|
24
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(12_000) });
|
|
25
|
+
if (!res.ok)
|
|
26
|
+
return { type: "image", data: "", mimeType };
|
|
27
|
+
const buf = await res.arrayBuffer();
|
|
28
|
+
const bytes = new Uint8Array(buf);
|
|
29
|
+
let bin = "";
|
|
30
|
+
for (let i = 0; i < bytes.length; i += 0x8000) {
|
|
31
|
+
bin += String.fromCharCode(...bytes.subarray(i, i + 0x8000));
|
|
32
|
+
}
|
|
33
|
+
return { type: "image", data: btoa(bin), mimeType };
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return { type: "image", data: "", mimeType };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
21
39
|
/**
|
|
22
40
|
* Rewrite external image URLs to go through the orchyn proxy so they
|
|
23
41
|
* work inside ChatGPT's sandboxed iframe (CORS + CSP restrictions).
|
|
@@ -52,7 +70,7 @@ function proxyUrls(obj) {
|
|
|
52
70
|
if (obj && typeof obj === "object") {
|
|
53
71
|
const out = {};
|
|
54
72
|
for (const [k, v] of Object.entries(obj)) {
|
|
55
|
-
if (["thumbnailUrl", "preview_url", "coverUrl", "avatarUrl", "avatar_thumb", "image_url", "url"].includes(k) && typeof v === "string") {
|
|
73
|
+
if (["thumbnailUrl", "preview_url", "coverUrl", "avatarUrl", "avatar_thumb", "image_url", "videoUrl", "video_url", "url"].includes(k) && typeof v === "string") {
|
|
56
74
|
out[k] = proxyImageUrl(v);
|
|
57
75
|
}
|
|
58
76
|
else {
|
|
@@ -63,14 +81,15 @@ function proxyUrls(obj) {
|
|
|
63
81
|
}
|
|
64
82
|
return obj;
|
|
65
83
|
}
|
|
66
|
-
function toToolResult(proxy) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
84
|
+
async function toToolResult(proxy) {
|
|
85
|
+
// The MCP SDK's ImageContent only accepts `data` (base64). The orchyn
|
|
86
|
+
// backend now emits `url` image blocks pointing at permanent, re-hosted
|
|
87
|
+
// JPEG thumbnails (HEIC already transcoded), so convert those URLs to
|
|
88
|
+
// base64 data here — guaranteeing clients render a valid image.
|
|
89
|
+
const imgBlocks = proxy.contentBlocks.filter((c) => c.type === "image");
|
|
90
|
+
const images = await Promise.all(imgBlocks.map((c) => c.url
|
|
91
|
+
? fetchAsBase64Image(String(c.url), String(c.mimeType ?? "image/jpeg"))
|
|
92
|
+
: Promise.resolve({ type: "image", data: String(c.data ?? ""), mimeType: String(c.mimeType ?? "image/jpeg") })));
|
|
74
93
|
// The Rust backend embeds HTML cards directly in the text block
|
|
75
94
|
// (type:"text", text:"<div>...</div>\n\n{json}"). We extract the HTML
|
|
76
95
|
// prefix from the first text contentBlock and prepend it.
|
|
@@ -91,9 +110,9 @@ function toToolResult(proxy) {
|
|
|
91
110
|
structuredContent: proxied ?? {},
|
|
92
111
|
};
|
|
93
112
|
}
|
|
94
|
-
/** Replace external image src
|
|
113
|
+
/** Replace external image/video src, poster and href URLs in HTML with proxied versions. */
|
|
95
114
|
function proxyImageUrlsInHtml(html) {
|
|
96
|
-
return html.replace(/(src|href)="(https?:\/\/[^"']+?)"/g, (_match, attr, url) => {
|
|
115
|
+
return html.replace(/(src|href|poster)="(https?:\/\/[^"']+?)"/g, (_match, attr, url) => {
|
|
97
116
|
return `${attr}="${proxyImageUrl(url)}"`;
|
|
98
117
|
});
|
|
99
118
|
}
|
|
@@ -101,19 +120,36 @@ function toolError(prefix, err) {
|
|
|
101
120
|
const msg = err instanceof Error ? err.message : String(err);
|
|
102
121
|
return { content: [{ type: "text", text: `${prefix}: ${msg}` }], isError: true };
|
|
103
122
|
}
|
|
104
|
-
/**
|
|
105
|
-
function
|
|
106
|
-
const
|
|
107
|
-
tiktok: "
|
|
108
|
-
|
|
123
|
+
/** Official brand color per platform (matches ui-template.ts pColor). */
|
|
124
|
+
function platformBrandColor(p) {
|
|
125
|
+
const map = {
|
|
126
|
+
tiktok: "#000000", douyin: "#000000", instagram: "#E4405F",
|
|
127
|
+
youtube: "#FF0000", xiaohongshu: "#FF2442", x: "#000000",
|
|
128
|
+
twitter: "#1DA1F2", bilibili: "#00A1D6", linkedin: "#0A66C2",
|
|
109
129
|
};
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
130
|
+
return map[p] || "#6b7280";
|
|
131
|
+
}
|
|
132
|
+
/** Official brand mark (simple-icons path) as an inline SVG inheriting currentColor. */
|
|
133
|
+
function platformSvgMark(p, size = 14) {
|
|
134
|
+
const paths = {
|
|
135
|
+
tiktok: "M12.525.02c1.31-.02 2.61-.01 3.91-.02.08 1.53.63 3.09 1.75 4.17 1.12 1.11 2.7 1.62 4.24 1.79v4.03c-1.44-.05-2.89-.35-4.2-.97-.57-.26-1.1-.59-1.62-.93-.01 2.92.01 5.84-.02 8.75-.08 1.4-.54 2.79-1.35 3.94-1.31 1.92-3.58 3.17-5.91 3.21-1.43.08-2.86-.31-4.08-1.03-2.02-1.19-3.44-3.37-3.65-5.71-.02-.5-.03-1-.01-1.49.18-1.9 1.12-3.72 2.58-4.96 1.66-1.44 3.98-2.13 6.15-1.72.02 1.48-.04 2.96-.04 4.44-.99-.32-2.15-.23-3.02.37-.63.41-1.11 1.04-1.36 1.75-.21.51-.15 1.07-.14 1.61.24 1.64 1.82 3.02 3.5 2.87 1.12-.01 2.19-.66 2.77-1.61.19-.33.4-.67.41-1.06.1-1.79.06-3.57.07-5.36.01-4.03-.01-8.05.02-12.07z",
|
|
136
|
+
douyin: "M12.525.02c1.31-.02 2.61-.01 3.91-.02.08 1.53.63 3.09 1.75 4.17 1.12 1.11 2.7 1.62 4.24 1.79v4.03c-1.44-.05-2.89-.35-4.2-.97-.57-.26-1.1-.59-1.62-.93-.01 2.92.01 5.84-.02 8.75-.08 1.4-.54 2.79-1.35 3.94-1.31 1.92-3.58 3.17-5.91 3.21-1.43.08-2.86-.31-4.08-1.03-2.02-1.19-3.44-3.37-3.65-5.71-.02-.5-.03-1-.01-1.49.18-1.9 1.12-3.72 2.58-4.96 1.66-1.44 3.98-2.13 6.15-1.72.02 1.48-.04 2.96-.04 4.44-.99-.32-2.15-.23-3.02.37-.63.41-1.11 1.04-1.36 1.75-.21.51-.15 1.07-.14 1.61.24 1.64 1.82 3.02 3.5 2.87 1.12-.01 2.19-.66 2.77-1.61.19-.33.4-.67.41-1.06.1-1.79.06-3.57.07-5.36.01-4.03-.01-8.05.02-12.07z",
|
|
137
|
+
instagram: "M7.0301.084c-1.2768.0602-2.1487.264-2.911.5634-.7888.3075-1.4575.72-2.1228 1.3877-.6652.6677-1.075 1.3368-1.3802 2.127-.2954.7638-.4956 1.6365-.552 2.914-.0564 1.2775-.0689 1.6882-.0626 4.947.0062 3.2586.0206 3.6671.0825 4.9473.061 1.2765.264 2.1482.5635 2.9107.308.7889.72 1.4573 1.388 2.1228.6679.6655 1.3365 1.0743 2.1285 1.38.7632.295 1.6361.4961 2.9134.552 1.2773.056 1.6884.069 4.9462.0627 3.2578-.0062 3.668-.0207 4.9478-.0814 1.28-.0607 2.147-.2652 2.9098-.5633.7889-.3086 1.4578-.72 2.1228-1.3881.665-.6682 1.0745-1.3378 1.3795-2.1284.2957-.7632.4966-1.636.552-2.9124.056-1.2809.0692-1.6898.063-4.948-.0063-3.2583-.021-3.6668-.0817-4.9465-.0607-1.2797-.264-2.1487-.5633-2.9117-.3084-.7889-.72-1.4568-1.3876-2.1228C21.2982 1.33 20.628.9208 19.8378.6165 19.074.321 18.2017.1197 16.9244.0645 15.6471.0093 15.236-.005 11.977.0014 8.718.0076 8.31.0215 7.0301.0839m.1402 21.6932c-1.17-.0509-1.8053-.2453-2.2287-.408-.5606-.216-.96-.4771-1.3819-.895-.422-.4178-.6811-.8186-.9-1.378-.1644-.4234-.3624-1.058-.4171-2.228-.0595-1.2645-.072-1.6442-.079-4.848-.007-3.2037.0053-3.583.0607-4.848.05-1.169.2456-1.805.408-2.2282.216-.5613.4762-.96.895-1.3816.4188-.4217.8184-.6814 1.3783-.9003.423-.1651 1.0575-.3614 2.227-.4171 1.2655-.06 1.6447-.072 4.848-.079 3.2033-.007 3.5835.005 4.8495.0608 1.169.0508 1.8053.2445 2.228.408.5608.216.96.4754 1.3816.895.4217.4194.6816.8176.9005 1.3787.1653.4217.3617 1.056.4169 2.2263.0602 1.2655.0739 1.645.0796 4.848.0058 3.203-.0055 3.5834-.061 4.848-.051 1.17-.245 1.8055-.408 2.2294-.216.5604-.4763.96-.8954 1.3814-.419.4215-.8181.6811-1.3783.9-.4224.1649-1.0577.3617-2.2262.4174-1.2656.0595-1.6448.072-4.8493.079-3.2045.007-3.5825-.006-4.848-.0608M16.953 5.5864A1.44 1.44 0 1 0 18.39 4.144a1.44 1.44 0 0 0-1.437 1.4424M5.8385 12.012c.0067 3.4032 2.7706 6.1557 6.173 6.1493 3.4026-.0065 6.157-2.7701 6.1506-6.1733-.0065-3.4032-2.771-6.1565-6.174-6.1498-3.403.0067-6.156 2.771-6.1496 6.1738M8 12.0077a4 4 0 1 1 4.008 3.9921A3.9996 3.9996 0 0 1 8 12.0077",
|
|
138
|
+
youtube: "M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z",
|
|
139
|
+
x: "M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.638 7.584H.474l8.6-9.83L0 1.154h7.594l5.243 6.932ZM17.61 20.644h2.039L6.486 3.24H4.298Z",
|
|
140
|
+
twitter: "M21.543 7.104c.015.211.015.423.015.636 0 6.507-4.954 14.01-14.01 14.01v-.003A13.94 13.94 0 0 1 0 19.539a9.88 9.88 0 0 0 7.287-2.041 4.93 4.93 0 0 1-4.6-3.42 4.916 4.916 0 0 0 2.223-.084A4.926 4.926 0 0 1 .96 9.167v-.062a4.887 4.887 0 0 0 2.235.616A4.928 4.928 0 0 1 1.67 3.148 13.98 13.98 0 0 0 11.82 8.292a4.929 4.929 0 0 1 8.39-4.49 9.868 9.868 0 0 0 3.128-1.196 4.941 4.941 0 0 1-2.165 2.724A9.828 9.828 0 0 0 24 4.555a10.019 10.019 0 0 1-2.457 2.549z",
|
|
141
|
+
xiaohongshu: "M22.405 9.879c.002.016.01.02.07.019h.725a.797.797 0 0 0 .78-.972.794.794 0 0 0-.884-.618.795.795 0 0 0-.692.794c0 .101-.002.666.001.777zm-11.509 4.808c-.203.001-1.353.004-1.685.003a2.528 2.528 0 0 1-.766-.126.025.025 0 0 0-.03.014L7.7 16.127a.025.025 0 0 0 .01.032c.111.06.336.124.495.124.66.01 1.32.002 1.981 0 .01 0 .02-.006.023-.015l.712-1.545a.025.025 0 0 0-.024-.036zM.477 9.91c-.071 0-.076.002-.076.01a.834.834 0 0 0-.01.08c-.027.397-.038.495-.234 3.06-.012.24-.034.389-.135.607-.026.057-.033.042.003.112.046.092.681 1.523.787 1.74.008.015.011.02.017.02.008 0 .033-.026.047-.044.147-.187.268-.391.371-.606.306-.635.44-1.325.486-1.706.014-.11.021-.22.03-.33l.204-2.616.022-.293c.003-.029 0-.033-.03-.034zm7.203 3.757a1.427 1.427 0 0 1-.135-.607c-.004-.084-.031-.39-.235-3.06a.443.443 0 0 0-.01-.082c-.004-.011-.052-.008-.076-.008h-1.48c-.03.001-.034.005-.03.034l.021.293c.076.982.153 1.964.233 2.946.05.4.186 1.085.487 1.706.103.215.223.419.37.606.015.018.037.051.048.049.02-.003.742-1.642.804-1.765.036-.07.03-.055.003-.112zm3.861-.913h-.872a.126.126 0 0 1-.116-.178l1.178-2.625a.025.025 0 0 0-.023-.035l-1.318-.003a.148.148 0 0 1-.135-.21l.876-1.954a.025.025 0 0 0-.023-.035h-1.56c-.01 0-.02.006-.024.015l-.926 2.068c-.085.169-.314.634-.399.938a.534.534 0 0 0-.02.191.46.46 0 0 0 .23.378.981.981 0 0 0 .46.119h.59c.041 0-.688 1.482-.834 1.972a.53.53 0 0 0-.023.172.465.465 0 0 0 .23.398c.15.092.342.12.475.12l1.66-.001c.01 0 .02-.006.023-.015l.575-1.28a.025.025 0 0 0-.024-.035zm-6.93-4.937H3.1a.032.032 0 0 0-.034.033c0 1.048-.01 2.795-.01 6.829 0 .288-.269.262-.28.262h-.74c-.04.001-.044.004-.04.047.001.037.465 1.064.555 1.263.01.02.03.033.051.033.157.003.767.009.938-.014.153-.02.3-.06.438-.132.3-.156.49-.419.595-.765.052-.172.075-.353.075-.533.002-2.33 0-4.66-.007-6.991a.032.032 0 0 0-.032-.032zm11.784 6.896c0-.014-.01-.021-.024-.022h-1.465c-.048-.001-.049-.002-.05-.049v-4.66c0-.072-.005-.07.07-.07h.863c.08 0 .075.004.075-.074V8.393c0-.082.006-.076-.08-.076h-3.5c-.064 0-.075-.006-.075.073v1.445c0 .083-.006.077.08.077h.854c.075 0 .07-.004.07.07v4.624c0 .095.008.084-.085.084-.37 0-1.11-.002-1.304 0-.048.001-.06.03-.06.03l-.697 1.519s-.014.025-.008.036c.006.01.013.008.058.008 1.748.003 3.495.002 5.243.002.03-.001.034-.006.035-.033v-1.539zm4.177-3.43c0 .013-.007.023-.02.024-.346.006-.692.004-1.037.004-.014-.002-.022-.01-.022-.024-.005-.434-.007-.869-.01-1.303 0-.072-.006-.071.07-.07l.733-.003c.041 0 .081.002.12.015.093.025.16.107.165.204.006.431.002 1.153.001 1.153zm2.67.244a1.953 1.953 0 0 0-.883-.222h-.18c-.04-.001-.04-.003-.042-.04V10.21c0-.132-.007-.263-.025-.394a1.823 1.823 0 0 0-.153-.53 1.533 1.533 0 0 0-.677-.71 2.167 2.167 0 0 0-1-.258c-.153-.003-.567 0-.72 0-.07 0-.068.004-.068-.065V7.76c0-.031-.01-.041-.046-.039H17.93s-.016 0-.023.007c-.006.006-.008.012-.008.023v.546c-.008.036-.057.015-.082.022h-.95c-.022.002-.028.008-.03.032v1.481c0 .09-.004.082.082.082h.913c.082 0 .072.128.072.128V11.19s.003.117-.06.117h-1.482c-.068 0-.06.082-.06.082v1.445s-.01.068.064.068h1.457c.082 0 .076-.006.076.079v3.225c0 .088-.007.081.082.081h1.43c.09 0 .082.007.082-.08v-3.27c0-.029.006-.035.033-.035l2.323-.003c.098 0 .191.02.28.061a.46.46 0 0 1 .274.407c.008.395.003.79.003 1.185 0 .259-.107.367-.33.367h-1.218c-.023.002-.029.008-.028.033.184.437.374.871.57 1.303a.045.045 0 0 0 .04.026c.17.005.34.002.51.003.15-.002.517.004.666-.01a2.03 2.03 0 0 0 .408-.075c.59-.18.975-.698.976-1.313v-1.981c0-.128-.01-.254-.034-.38 0 .078-.029-.641-.724-.998z",
|
|
142
|
+
bilibili: "M17.813 4.653h.854c1.51.054 2.769.578 3.773 1.574 1.004.995 1.524 2.249 1.56 3.76v7.36c-.036 1.51-.556 2.769-1.56 3.773s-2.262 1.524-3.773 1.56H5.333c-1.51-.036-2.769-.556-3.773-1.56S.036 18.858 0 17.347v-7.36c.036-1.511.556-2.765 1.56-3.76 1.004-.996 2.262-1.52 3.773-1.574h.774l-1.174-1.12a1.234 1.234 0 0 1-.373-.906c0-.356.124-.658.373-.907l.027-.027c.267-.249.573-.373.92-.373.347 0 .653.124.92.373L9.653 4.44c.071.071.134.142.187.213h4.267a.836.836 0 0 1 .16-.213l2.853-2.747c.267-.249.573-.373.92-.373.347 0 .662.151.929.4.267.249.391.551.391.907 0 .355-.124.657-.373.906zM5.333 7.24c-.746.018-1.373.276-1.88.773-.506.498-.769 1.13-.786 1.894v7.52c.017.764.28 1.395.786 1.893.507.498 1.134.756 1.88.773h13.334c.746-.017 1.373-.275 1.88-.773.506-.498.769-1.129.786-1.893v-7.52c-.017-.765-.28-1.396-.786-1.894-.507-.497-1.134-.755-1.88-.773zM8 11.107c.373 0 .684.124.933.373.25.249.383.569.4.96v1.173c-.017.391-.15.711-.4.96-.249.25-.56.374-.933.374s-.684-.125-.933-.374c-.25-.249-.383-.569-.4-.96V12.44c0-.373.129-.689.386-.947.258-.257.574-.386.947-.386zm8 0c.373 0 .684.124.933.373.25.249.383.569.4.96v1.173c-.017.391-.15.711-.4.96-.249.25-.56.374-.933.374s-.684-.125-.933-.374c-.25-.249-.383-.569-.4-.96V12.44c.017-.391.15-.711.4-.96.249-.249.56-.373.933-.373Z",
|
|
143
|
+
linkedin: "M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z",
|
|
144
|
+
unknown: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71",
|
|
114
145
|
};
|
|
115
|
-
const
|
|
116
|
-
|
|
146
|
+
const d = paths[p] || paths.unknown;
|
|
147
|
+
return `<svg viewBox="0 0 24 24" width="${size}" height="${size}" fill="currentColor" style="vertical-align:-2px;flex-shrink:0;display:inline-block" aria-hidden="true"><path d="${d}"/></svg>`;
|
|
148
|
+
}
|
|
149
|
+
/** Build an HTML card for analyze_post results. */
|
|
150
|
+
function buildAnalysisHtmlCard(result, thumbnailUrl, platform, creatorHandle, title) {
|
|
151
|
+
const brandSvg = platformSvgMark(platform, 14);
|
|
152
|
+
const color = platformBrandColor(platform);
|
|
117
153
|
const post = (result.post ?? {});
|
|
118
154
|
const analysis = (result.analysis ?? {});
|
|
119
155
|
const views = typeof post.views === "number" ? post.views.toLocaleString() : "—";
|
|
@@ -126,12 +162,19 @@ function buildAnalysisHtmlCard(result, thumbnailUrl, platform, creatorHandle, ti
|
|
|
126
162
|
const viralTriggers = Array.isArray(analysis.viralTriggers) ? analysis.viralTriggers : [];
|
|
127
163
|
const variationIdeas = Array.isArray(analysis.variationIdeas) ? analysis.variationIdeas.slice(0, 3) : [];
|
|
128
164
|
const suggestedHook = typeof analysis.suggestedHook === "string" ? analysis.suggestedHook : "";
|
|
165
|
+
const videoUrl = typeof post.videoUrl === "string"
|
|
166
|
+
? post.videoUrl
|
|
167
|
+
: Array.isArray(post.mediaItems)
|
|
168
|
+
? post.mediaItems.find((m) => m.kind === "video" && typeof m.preview_url === "string")?.preview_url
|
|
169
|
+
: undefined;
|
|
129
170
|
let html = `<div style="font-family:system-ui,-apple-system,sans-serif;max-width:600px;background:#0d1117;color:#e6edf3;border-radius:12px;overflow:hidden;margin:0 auto">`;
|
|
130
|
-
// Thumbnail
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
html += `<
|
|
134
|
-
|
|
171
|
+
// Thumbnail / video
|
|
172
|
+
const badgeHtml = `<span style="background:${color};color:#fff;padding:4px 10px;border-radius:999px;font-size:12px;font-weight:600;display:inline-flex;align-items:center;gap:4px">${brandSvg} ${platform}</span>`;
|
|
173
|
+
if (videoUrl) {
|
|
174
|
+
html += `<div style="position:relative"><video src="${videoUrl}" controls preload="metadata" playsinline poster="${thumbnailUrl}" style="width:100%;max-height:400px;object-fit:contain;display:block;background:#000" onerror="this.outerHTML='<img src="${thumbnailUrl}" style="width:100%;max-height:400px;object-fit:cover;display:block"/>'">Your browser doesn't support video playback.</video><div style="position:absolute;bottom:12px;left:12px;display:flex;gap:8px;flex-wrap:wrap">${badgeHtml}</div></div>`;
|
|
175
|
+
}
|
|
176
|
+
else if (thumbnailUrl) {
|
|
177
|
+
html += `<div style="position:relative"><img src="${thumbnailUrl}" style="width:100%;max-height:400px;object-fit:cover;display:block" onerror="this.style.display='none'" /><div style="position:absolute;bottom:12px;left:12px;display:flex;gap:8px;flex-wrap:wrap">${badgeHtml}</div></div>`;
|
|
135
178
|
}
|
|
136
179
|
// Header
|
|
137
180
|
html += `<div style="padding:16px 20px">`;
|
|
@@ -201,6 +244,8 @@ export function createMcpServer(makeClient) {
|
|
|
201
244
|
"https://*.cdninstagram.com",
|
|
202
245
|
"https://*.ytimg.com",
|
|
203
246
|
"https://*.googlevideo.com",
|
|
247
|
+
"https://*.licdn.com",
|
|
248
|
+
"https://*.linkedin.com",
|
|
204
249
|
];
|
|
205
250
|
const apiUrl = process.env.ORCHYN_API_URL || process.env.ORCHYN_BASE_URL;
|
|
206
251
|
if (apiUrl && apiUrl.trim()) {
|
|
@@ -257,11 +302,14 @@ export function createMcpServer(makeClient) {
|
|
|
257
302
|
const creatorHandle = String(post.creatorHandle ?? "");
|
|
258
303
|
const title = String(post.title ?? post.caption ?? "");
|
|
259
304
|
const htmlCard = buildAnalysisHtmlCard(proxied, thumbnailUrl, platform, creatorHandle, title);
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
305
|
+
// The backend attaches `_inlineImages` as permanent orchyn public URLs
|
|
306
|
+
// (re-hosted into storage, HEIC already transcoded to JPEG). The SDK's
|
|
307
|
+
// ImageContent only accepts base64 `data`, so fetch each URL and encode it.
|
|
308
|
+
const images = await Promise.all((result.inlineImages ?? [])
|
|
309
|
+
.filter((img) => typeof img === "object" && img !== null)
|
|
310
|
+
.map((img) => "url" in img && img.url
|
|
311
|
+
? fetchAsBase64Image(String(img.url), String(img.mimeType ?? "image/jpeg"))
|
|
312
|
+
: Promise.resolve({ type: "image", data: String(img.data ?? ""), mimeType: String(img.mimeType ?? "image/jpeg") })));
|
|
265
313
|
const textJson = JSON.stringify(proxied, null, 2);
|
|
266
314
|
return {
|
|
267
315
|
content: [...images, { type: "text", text: `${htmlCard}\n\n${textJson}` }],
|
|
@@ -293,7 +341,7 @@ export function createMcpServer(makeClient) {
|
|
|
293
341
|
}, async (args, extra) => {
|
|
294
342
|
const client = await makeClient(extra);
|
|
295
343
|
try {
|
|
296
|
-
return toToolResult(await client.callTool("get_social_media", { url: args.url }));
|
|
344
|
+
return await toToolResult(await client.callTool("get_social_media", { url: args.url }));
|
|
297
345
|
}
|
|
298
346
|
catch (err) {
|
|
299
347
|
return toolError("get_social_media failed", err);
|
|
@@ -320,7 +368,7 @@ export function createMcpServer(makeClient) {
|
|
|
320
368
|
}, async (args, extra) => {
|
|
321
369
|
const client = await makeClient(extra);
|
|
322
370
|
try {
|
|
323
|
-
return toToolResult(await client.callTool("discover_social_posts", { ...args }));
|
|
371
|
+
return await toToolResult(await client.callTool("discover_social_posts", { ...args }));
|
|
324
372
|
}
|
|
325
373
|
catch (err) {
|
|
326
374
|
return toolError("discover_social_posts failed", err);
|
|
@@ -328,7 +376,7 @@ export function createMcpServer(makeClient) {
|
|
|
328
376
|
});
|
|
329
377
|
server.registerTool("get_user_posts", {
|
|
330
378
|
title: "Get User Posts",
|
|
331
|
-
description: "List recent posts by a creator handle (e.g. @zoundsapp) on TikTok, Instagram, YouTube, Douyin, Xiaohongshu, X/Twitter or
|
|
379
|
+
description: "List recent posts by a creator handle (e.g. @zoundsapp) on TikTok, Instagram, YouTube, Douyin, Xiaohongshu, X/Twitter, Bilibili or LinkedIn (LinkedIn uses the profile public_id, e.g. 'billgates'). " +
|
|
332
380
|
"Each post includes title/caption, thumbnailUrl, externalUrl, views/likes/comments and inline thumbnails (up to 4) so they show in chat. " +
|
|
333
381
|
"Use this when Claude needs to pull more posts from the same account to spot a pattern, or to scan a whole profile. Consumes 2 orchyn credits. First use free per user.",
|
|
334
382
|
_meta: { ui: { resourceUri: UI_RESOURCE_URI } },
|
|
@@ -336,7 +384,7 @@ export function createMcpServer(makeClient) {
|
|
|
336
384
|
.object({
|
|
337
385
|
username: z.string().describe("Creator handle, e.g. 'zoundsapp' or '@zoundsapp'."),
|
|
338
386
|
platform: z
|
|
339
|
-
.enum(["tiktok", "instagram", "youtube", "douyin", "xiaohongshu", "twitter", "bilibili"])
|
|
387
|
+
.enum(["tiktok", "instagram", "youtube", "douyin", "xiaohongshu", "twitter", "bilibili", "linkedin"])
|
|
340
388
|
.optional()
|
|
341
389
|
.describe("Which platform (default tiktok)."),
|
|
342
390
|
limit: z.number().int().optional().describe("Max posts (default 6)."),
|
|
@@ -345,7 +393,7 @@ export function createMcpServer(makeClient) {
|
|
|
345
393
|
}, async (args, extra) => {
|
|
346
394
|
const client = await makeClient(extra);
|
|
347
395
|
try {
|
|
348
|
-
return toToolResult(await client.callTool("get_user_posts", { ...args }));
|
|
396
|
+
return await toToolResult(await client.callTool("get_user_posts", { ...args }));
|
|
349
397
|
}
|
|
350
398
|
catch (err) {
|
|
351
399
|
return toolError("get_user_posts failed", err);
|
|
@@ -353,7 +401,7 @@ export function createMcpServer(makeClient) {
|
|
|
353
401
|
});
|
|
354
402
|
server.registerTool("analyze_creator_profile", {
|
|
355
403
|
title: "Analyze Creator Profile",
|
|
356
|
-
description: "Deep-dive a whole creator profile on TikTok, Instagram, YouTube, Douyin, Xiaohongshu, X/Twitter or
|
|
404
|
+
description: "Deep-dive a whole creator profile on TikTok, Instagram, YouTube, Douyin, Xiaohongshu, X/Twitter, Bilibili or LinkedIn: fetch recent posts, run multimodal AI on up to 3, " +
|
|
357
405
|
"then synthesize a profile report — creator summary, niche, content themes, hook styles, strengths/weaknesses, " +
|
|
358
406
|
"engagement patterns, audience insights, variation ideas, collaboration fit. Consumes 15 orchyn credits. First use free per user.",
|
|
359
407
|
_meta: { ui: { resourceUri: UI_RESOURCE_URI } },
|
|
@@ -361,7 +409,7 @@ export function createMcpServer(makeClient) {
|
|
|
361
409
|
.object({
|
|
362
410
|
username: z.string().describe("Creator handle, e.g. 'zoundsapp'."),
|
|
363
411
|
platform: z
|
|
364
|
-
.enum(["tiktok", "instagram", "youtube", "douyin", "xiaohongshu", "twitter", "bilibili"])
|
|
412
|
+
.enum(["tiktok", "instagram", "youtube", "douyin", "xiaohongshu", "twitter", "bilibili", "linkedin"])
|
|
365
413
|
.optional()
|
|
366
414
|
.describe("Which platform (default tiktok)."),
|
|
367
415
|
limit: z.number().int().optional().describe("Posts to fetch (default 6; first 3 analyzed)."),
|
|
@@ -371,7 +419,7 @@ export function createMcpServer(makeClient) {
|
|
|
371
419
|
}, async (args, extra) => {
|
|
372
420
|
const client = await makeClient(extra);
|
|
373
421
|
try {
|
|
374
|
-
return toToolResult(await client.callTool("analyze_creator_profile", { ...args }));
|
|
422
|
+
return await toToolResult(await client.callTool("analyze_creator_profile", { ...args }));
|
|
375
423
|
}
|
|
376
424
|
catch (err) {
|
|
377
425
|
return toolError("analyze_creator_profile failed", err);
|
|
@@ -379,19 +427,19 @@ export function createMcpServer(makeClient) {
|
|
|
379
427
|
});
|
|
380
428
|
server.registerTool("get_post_comments", {
|
|
381
429
|
title: "Get Post Comments",
|
|
382
|
-
description: "Fetch top comments for a post URL on TikTok, Instagram, YouTube, Douyin, X/Twitter or
|
|
430
|
+
description: "Fetch top comments for a post URL on TikTok, Instagram, YouTube, Douyin, X/Twitter, Bilibili or LinkedIn, plus keyword clusters from TikTok Analytics " +
|
|
383
431
|
"when available — audience sentiment/audience-signal analysis. Consumes 2 orchyn credits. First use free per user.",
|
|
384
432
|
_meta: { ui: { resourceUri: UI_RESOURCE_URI } },
|
|
385
433
|
inputSchema: z
|
|
386
434
|
.object({
|
|
387
|
-
url: z.string().describe("Full public post URL (TikTok/Instagram/YouTube/Douyin/X/Bilibili)."),
|
|
435
|
+
url: z.string().describe("Full public post URL (TikTok/Instagram/YouTube/Douyin/X/Bilibili/LinkedIn)."),
|
|
388
436
|
limit: z.number().int().optional().describe("Max comments (default 20)."),
|
|
389
437
|
})
|
|
390
438
|
.strict(),
|
|
391
439
|
}, async (args, extra) => {
|
|
392
440
|
const client = await makeClient(extra);
|
|
393
441
|
try {
|
|
394
|
-
return toToolResult(await client.callTool("get_post_comments", { ...args }));
|
|
442
|
+
return await toToolResult(await client.callTool("get_post_comments", { ...args }));
|
|
395
443
|
}
|
|
396
444
|
catch (err) {
|
|
397
445
|
return toolError("get_post_comments failed", err);
|
|
@@ -415,7 +463,7 @@ export function createMcpServer(makeClient) {
|
|
|
415
463
|
}, async (args, extra) => {
|
|
416
464
|
const client = await makeClient(extra);
|
|
417
465
|
try {
|
|
418
|
-
return toToolResult(await client.callTool("search_creators", { ...args }));
|
|
466
|
+
return await toToolResult(await client.callTool("search_creators", { ...args }));
|
|
419
467
|
}
|
|
420
468
|
catch (err) {
|
|
421
469
|
return toolError("search_creators failed", err);
|
|
@@ -435,7 +483,7 @@ export function createMcpServer(makeClient) {
|
|
|
435
483
|
}, async (args, extra) => {
|
|
436
484
|
const client = await makeClient(extra);
|
|
437
485
|
try {
|
|
438
|
-
return toToolResult(await client.callTool("get_similar_creators", { ...args }));
|
|
486
|
+
return await toToolResult(await client.callTool("get_similar_creators", { ...args }));
|
|
439
487
|
}
|
|
440
488
|
catch (err) {
|
|
441
489
|
return toolError("get_similar_creators failed", err);
|
|
@@ -456,7 +504,7 @@ export function createMcpServer(makeClient) {
|
|
|
456
504
|
}, async (args, extra) => {
|
|
457
505
|
const client = await makeClient(extra);
|
|
458
506
|
try {
|
|
459
|
-
return toToolResult(await client.callTool("discover_sounds", { ...args }));
|
|
507
|
+
return await toToolResult(await client.callTool("discover_sounds", { ...args }));
|
|
460
508
|
}
|
|
461
509
|
catch (err) {
|
|
462
510
|
return toolError("discover_sounds failed", err);
|
|
@@ -470,7 +518,7 @@ export function createMcpServer(makeClient) {
|
|
|
470
518
|
}, async (_args, extra) => {
|
|
471
519
|
const client = await makeClient(extra);
|
|
472
520
|
try {
|
|
473
|
-
return toToolResult(await client.callTool("check_orchyn_credits", {}));
|
|
521
|
+
return await toToolResult(await client.callTool("check_orchyn_credits", {}));
|
|
474
522
|
}
|
|
475
523
|
catch (err) {
|
|
476
524
|
return toolError("check_orchyn_credits failed", err);
|
|
@@ -484,7 +532,7 @@ export function createMcpServer(makeClient) {
|
|
|
484
532
|
}, async (_args, extra) => {
|
|
485
533
|
const client = await makeClient(extra);
|
|
486
534
|
try {
|
|
487
|
-
return toToolResult(await client.callTool("buy_orchyn_credits", {}));
|
|
535
|
+
return await toToolResult(await client.callTool("buy_orchyn_credits", {}));
|
|
488
536
|
}
|
|
489
537
|
catch (err) {
|
|
490
538
|
return toolError("buy_orchyn_credits failed", err);
|
|
@@ -508,7 +556,7 @@ export function createMcpServer(makeClient) {
|
|
|
508
556
|
}, async (args, extra) => {
|
|
509
557
|
const client = await makeClient(extra);
|
|
510
558
|
try {
|
|
511
|
-
return toToolResult(await client.callTool("understand_social_post", { ...args }));
|
|
559
|
+
return await toToolResult(await client.callTool("understand_social_post", { ...args }));
|
|
512
560
|
}
|
|
513
561
|
catch (err) {
|
|
514
562
|
return toolError("understand_social_post failed", err);
|
|
@@ -75,7 +75,7 @@ body{font-family:system-ui,-apple-system,'Segoe UI',sans-serif;background:var(--
|
|
|
75
75
|
.gallery{display:flex;flex-wrap:wrap;gap:10px;}
|
|
76
76
|
.card{border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;width:300px;background:var(--card);display:inline-block;vertical-align:top;box-shadow:var(--shadow-sm);transition:var(--transition);cursor:default;}
|
|
77
77
|
.card:hover{box-shadow:var(--shadow-md);transform:translateY(-2px);border-color:var(--muted);}
|
|
78
|
-
.card-wide{
|
|
78
|
+
.card-wide{width:100%;}
|
|
79
79
|
.card img{width:100%;height:200px;object-fit:cover;display:block;transition:transform .3s ease;}
|
|
80
80
|
.card:hover img{transform:scale(1.02);}
|
|
81
81
|
.card-wide img{max-height:340px;}
|
|
@@ -221,10 +221,22 @@ body{font-family:system-ui,-apple-system,'Segoe UI',sans-serif;background:var(--
|
|
|
221
221
|
}
|
|
222
222
|
});
|
|
223
223
|
|
|
224
|
-
// Show
|
|
224
|
+
// Show loading state after 300ms — branded spinner until tool name arrives,
|
|
225
|
+
// then switch to the correct skeleton type.
|
|
225
226
|
loadingTimer=setTimeout(function(){
|
|
226
227
|
var app=document.getElementById("app");
|
|
227
|
-
if(app)
|
|
228
|
+
if(!app)return;
|
|
229
|
+
if(currentTool){
|
|
230
|
+
// Tool name already known → show correct skeleton
|
|
231
|
+
app.innerHTML=renderSkeleton(currentTool);
|
|
232
|
+
}else{
|
|
233
|
+
// Tool name not yet known → show branded spinner (neutral)
|
|
234
|
+
app.innerHTML='<div class="loading-container fade-in">'
|
|
235
|
+
+'<div class="orchyn-logo"><svg width="40" height="40" viewBox="0 0 48 48" fill="none"><g fill="var(--accent)" transform="translate(24 24)"><circle r="4.1"/><g id="rl"><path d="M-2.85 -5.2 L0 -20.6 L2.85 -5.2 L1.15 1.1 L-1.15 1.1 Z"/></g><use href="#rl" transform="rotate(45)"/><use href="#rl" transform="rotate(90)"/><use href="#rl" transform="rotate(135)"/><use href="#rl" transform="rotate(180)"/><use href="#rl" transform="rotate(225)"/><use href="#rl" transform="rotate(270)"/><use href="#rl" transform="rotate(315)"/></g></svg></div>'
|
|
236
|
+
+'<div class="loading-brand">Orchyn</div>'
|
|
237
|
+
+'<div class="loading-text">Loading…</div>'
|
|
238
|
+
+'</div>';
|
|
239
|
+
}
|
|
228
240
|
},300);
|
|
229
241
|
|
|
230
242
|
// Fallback: if no tool-result arrives within 3s, check if data is embedded
|
|
@@ -269,8 +281,25 @@ body{font-family:system-ui,-apple-system,'Segoe UI',sans-serif;background:var(--
|
|
|
269
281
|
// ─── Helpers ───
|
|
270
282
|
function esc(s){return String(s||"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""");}
|
|
271
283
|
function fmtNum(n){n=Number(n)||0;return n>=1e6?(n/1e6).toFixed(1)+"M":n>=1e3?(n/1e3).toFixed(1)+"K":String(n);}
|
|
272
|
-
function pColor(p){return{tiktok:"#000",
|
|
273
|
-
|
|
284
|
+
function pColor(p){return{tiktok:"#000",douyin:"#000",instagram:"#E4405F",youtube:"#FF0000",xiaohongshu:"#FF2442",x:"#000",twitter:"#1DA1F2",bilibili:"#00A1D6",linkedin:"#0A66C2"}[p]||"#6B7280";}
|
|
285
|
+
// Official brand mark (simple-icons path) as an inline SVG that inherits
|
|
286
|
+
// the current text color via fill="currentColor" — matches badge accents.
|
|
287
|
+
function pSvg(p,size){
|
|
288
|
+
var s=size||14,paths={
|
|
289
|
+
tiktok:"M12.525.02c1.31-.02 2.61-.01 3.91-.02.08 1.53.63 3.09 1.75 4.17 1.12 1.11 2.7 1.62 4.24 1.79v4.03c-1.44-.05-2.89-.35-4.2-.97-.57-.26-1.1-.59-1.62-.93-.01 2.92.01 5.84-.02 8.75-.08 1.4-.54 2.79-1.35 3.94-1.31 1.92-3.58 3.17-5.91 3.21-1.43.08-2.86-.31-4.08-1.03-2.02-1.19-3.44-3.37-3.65-5.71-.02-.5-.03-1-.01-1.49.18-1.9 1.12-3.72 2.58-4.96 1.66-1.44 3.98-2.13 6.15-1.72.02 1.48-.04 2.96-.04 4.44-.99-.32-2.15-.23-3.02.37-.63.41-1.11 1.04-1.36 1.75-.21.51-.15 1.07-.14 1.61.24 1.64 1.82 3.02 3.5 2.87 1.12-.01 2.19-.66 2.77-1.61.19-.33.4-.67.41-1.06.1-1.79.06-3.57.07-5.36.01-4.03-.01-8.05.02-12.07z",
|
|
290
|
+
instagram:"M7.0301.084c-1.2768.0602-2.1487.264-2.911.5634-.7888.3075-1.4575.72-2.1228 1.3877-.6652.6677-1.075 1.3368-1.3802 2.127-.2954.7638-.4956 1.6365-.552 2.914-.0564 1.2775-.0689 1.6882-.0626 4.947.0062 3.2586.0206 3.6671.0825 4.9473.061 1.2765.264 2.1482.5635 2.9107.308.7889.72 1.4573 1.388 2.1228.6679.6655 1.3365 1.0743 2.1285 1.38.7632.295 1.6361.4961 2.9134.552 1.2773.056 1.6884.069 4.9462.0627 3.2578-.0062 3.668-.0207 4.9478-.0814 1.28-.0607 2.147-.2652 2.9098-.5633.7889-.3086 1.4578-.72 2.1228-1.3881.665-.6682 1.0745-1.3378 1.3795-2.1284.2957-.7632.4966-1.636.552-2.9124.056-1.2809.0692-1.6898.063-4.948-.0063-3.2583-.021-3.6668-.0817-4.9465-.0607-1.2797-.264-2.1487-.5633-2.9117-.3084-.7889-.72-1.4568-1.3876-2.1228C21.2982 1.33 20.628.9208 19.8378.6165 19.074.321 18.2017.1197 16.9244.0645 15.6471.0093 15.236-.005 11.977.0014 8.718.0076 8.31.0215 7.0301.0839m.1402 21.6932c-1.17-.0509-1.8053-.2453-2.2287-.408-.5606-.216-.96-.4771-1.3819-.895-.422-.4178-.6811-.8186-.9-1.378-.1644-.4234-.3624-1.058-.4171-2.228-.0595-1.2645-.072-1.6442-.079-4.848-.007-3.2037.0053-3.583.0607-4.848.05-1.169.2456-1.805.408-2.2282.216-.5613.4762-.96.895-1.3816.4188-.4217.8184-.6814 1.3783-.9003.423-.1651 1.0575-.3614 2.227-.4171 1.2655-.06 1.6447-.072 4.848-.079 3.2033-.007 3.5835.005 4.8495.0608 1.169.0508 1.8053.2445 2.228.408.5608.216.96.4754 1.3816.895.4217.4194.6816.8176.9005 1.3787.1653.4217.3617 1.056.4169 2.2263.0602 1.2655.0739 1.645.0796 4.848.0058 3.203-.0055 3.5834-.061 4.848-.051 1.17-.245 1.8055-.408 2.2294-.216.5604-.4763.96-.8954 1.3814-.419.4215-.8181.6811-1.3783.9-.4224.1649-1.0577.3617-2.2262.4174-1.2656.0595-1.6448.072-4.8493.079-3.2045.007-3.5825-.006-4.848-.0608M16.953 5.5864A1.44 1.44 0 1 0 18.39 4.144a1.44 1.44 0 0 0-1.437 1.4424M5.8385 12.012c.0067 3.4032 2.7706 6.1557 6.173 6.1493 3.4026-.0065 6.157-2.7701 6.1506-6.1733-.0065-3.4032-2.771-6.1565-6.174-6.1498-3.403.0067-6.156 2.771-6.1496 6.1738M8 12.0077a4 4 0 1 1 4.008 3.9921A3.9996 3.9996 0 0 1 8 12.0077",
|
|
291
|
+
youtube:"M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z",
|
|
292
|
+
x:"M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.638 7.584H.474l8.6-9.83L0 1.154h7.594l5.243 6.932ZM17.61 20.644h2.039L6.486 3.24H4.298Z",
|
|
293
|
+
twitter:"M21.543 7.104c.015.211.015.423.015.636 0 6.507-4.954 14.01-14.01 14.01v-.003A13.94 13.94 0 0 1 0 19.539a9.88 9.88 0 0 0 7.287-2.041 4.93 4.93 0 0 1-4.6-3.42 4.916 4.916 0 0 0 2.223-.084A4.926 4.926 0 0 1 .96 9.167v-.062a4.887 4.887 0 0 0 2.235.616A4.928 4.928 0 0 1 1.67 3.148 13.98 13.98 0 0 0 11.82 8.292a4.929 4.929 0 0 1 8.39-4.49 9.868 9.868 0 0 0 3.128-1.196 4.941 4.941 0 0 1-2.165 2.724A9.828 9.828 0 0 0 24 4.555a10.019 10.019 0 0 1-2.457 2.549z",
|
|
294
|
+
douyin:"M12.525.02c1.31-.02 2.61-.01 3.91-.02.08 1.53.63 3.09 1.75 4.17 1.12 1.11 2.7 1.62 4.24 1.79v4.03c-1.44-.05-2.89-.35-4.2-.97-.57-.26-1.1-.59-1.62-.93-.01 2.92.01 5.84-.02 8.75-.08 1.4-.54 2.79-1.35 3.94-1.31 1.92-3.58 3.17-5.91 3.21-1.43.08-2.86-.31-4.08-1.03-2.02-1.19-3.44-3.37-3.65-5.71-.02-.5-.03-1-.01-1.49.18-1.9 1.12-3.72 2.58-4.96 1.66-1.44 3.98-2.13 6.15-1.72.02 1.48-.04 2.96-.04 4.44-.99-.32-2.15-.23-3.02.37-.63.41-1.11 1.04-1.36 1.75-.21.51-.15 1.07-.14 1.61.24 1.64 1.82 3.02 3.5 2.87 1.12-.01 2.19-.66 2.77-1.61.19-.33.4-.67.41-1.06.1-1.79.06-3.57.07-5.36.01-4.03-.01-8.05.02-12.07z",
|
|
295
|
+
xiaohongshu:"M22.405 9.879c.002.016.01.02.07.019h.725a.797.797 0 0 0 .78-.972.794.794 0 0 0-.884-.618.795.795 0 0 0-.692.794c0 .101-.002.666.001.777zm-11.509 4.808c-.203.001-1.353.004-1.685.003a2.528 2.528 0 0 1-.766-.126.025.025 0 0 0-.03.014L7.7 16.127a.025.025 0 0 0 .01.032c.111.06.336.124.495.124.66.01 1.32.002 1.981 0 .01 0 .02-.006.023-.015l.712-1.545a.025.025 0 0 0-.024-.036zM.477 9.91c-.071 0-.076.002-.076.01a.834.834 0 0 0-.01.08c-.027.397-.038.495-.234 3.06-.012.24-.034.389-.135.607-.026.057-.033.042.003.112.046.092.681 1.523.787 1.74.008.015.011.02.017.02.008 0 .033-.026.047-.044.147-.187.268-.391.371-.606.306-.635.44-1.325.486-1.706.014-.11.021-.22.03-.33l.204-2.616.022-.293c.003-.029 0-.033-.03-.034zm7.203 3.757a1.427 1.427 0 0 1-.135-.607c-.004-.084-.031-.39-.235-3.06a.443.443 0 0 0-.01-.082c-.004-.011-.052-.008-.076-.008h-1.48c-.03.001-.034.005-.03.034l.021.293c.076.982.153 1.964.233 2.946.05.4.186 1.085.487 1.706.103.215.223.419.37.606.015.018.037.051.048.049.02-.003.742-1.642.804-1.765.036-.07.03-.055.003-.112zm3.861-.913h-.872a.126.126 0 0 1-.116-.178l1.178-2.625a.025.025 0 0 0-.023-.035l-1.318-.003a.148.148 0 0 1-.135-.21l.876-1.954a.025.025 0 0 0-.023-.035h-1.56c-.01 0-.02.006-.024.015l-.926 2.068c-.085.169-.314.634-.399.938a.534.534 0 0 0-.02.191.46.46 0 0 0 .23.378.981.981 0 0 0 .46.119h.59c.041 0-.688 1.482-.834 1.972a.53.53 0 0 0-.023.172.465.465 0 0 0 .23.398c.15.092.342.12.475.12l1.66-.001c.01 0 .02-.006.023-.015l.575-1.28a.025.025 0 0 0-.024-.035zm-6.93-4.937H3.1a.032.032 0 0 0-.034.033c0 1.048-.01 2.795-.01 6.829 0 .288-.269.262-.28.262h-.74c-.04.001-.044.004-.04.047.001.037.465 1.064.555 1.263.01.02.03.033.051.033.157.003.767.009.938-.014.153-.02.3-.06.438-.132.3-.156.49-.419.595-.765.052-.172.075-.353.075-.533.002-2.33 0-4.66-.007-6.991a.032.032 0 0 0-.032-.032zm11.784 6.896c0-.014-.01-.021-.024-.022h-1.465c-.048-.001-.049-.002-.05-.049v-4.66c0-.072-.005-.07.07-.07h.863c.08 0 .075.004.075-.074V8.393c0-.082.006-.076-.08-.076h-3.5c-.064 0-.075-.006-.075.073v1.445c0 .083-.006.077.08.077h.854c.075 0 .07-.004.07.07v4.624c0 .095.008.084-.085.084-.37 0-1.11-.002-1.304 0-.048.001-.06.03-.06.03l-.697 1.519s-.014.025-.008.036c.006.01.013.008.058.008 1.748.003 3.495.002 5.243.002.03-.001.034-.006.035-.033v-1.539zm4.177-3.43c0 .013-.007.023-.02.024-.346.006-.692.004-1.037.004-.014-.002-.022-.01-.022-.024-.005-.434-.007-.869-.01-1.303 0-.072-.006-.071.07-.07l.733-.003c.041 0 .081.002.12.015.093.025.16.107.165.204.006.431.002 1.153.001 1.153zm2.67.244a1.953 1.953 0 0 0-.883-.222h-.18c-.04-.001-.04-.003-.042-.04V10.21c0-.132-.007-.263-.025-.394a1.823 1.823 0 0 0-.153-.53 1.533 1.533 0 0 0-.677-.71 2.167 2.167 0 0 0-1-.258c-.153-.003-.567 0-.72 0-.07 0-.068.004-.068-.065V7.76c0-.031-.01-.041-.046-.039H17.93s-.016 0-.023.007c-.006.006-.008.012-.008.023v.546c-.008.036-.057.015-.082.022h-.95c-.022.002-.028.008-.03.032v1.481c0 .09-.004.082.082.082h.913c.082 0 .072.128.072.128V11.19s.003.117-.06.117h-1.482c-.068 0-.06.082-.06.082v1.445s-.01.068.064.068h1.457c.082 0 .076-.006.076.079v3.225c0 .088-.007.081.082.081h1.43c.09 0 .082.007.082-.08v-3.27c0-.029.006-.035.033-.035l2.323-.003c.098 0 .191.02.28.061a.46.46 0 0 1 .274.407c.008.395.003.79.003 1.185 0 .259-.107.367-.33.367h-1.218c-.023.002-.029.008-.028.033.184.437.374.871.57 1.303a.045.045 0 0 0 .04.026c.17.005.34.002.51.003.15-.002.517.004.666-.01a2.03 2.03 0 0 0 .408-.075c.59-.18.975-.698.976-1.313v-1.981c0-.128-.01-.254-.034-.38 0 .078-.029-.641-.724-.998z",
|
|
296
|
+
bilibili:"M17.813 4.653h.854c1.51.054 2.769.578 3.773 1.574 1.004.995 1.524 2.249 1.56 3.76v7.36c-.036 1.51-.556 2.769-1.56 3.773s-2.262 1.524-3.773 1.56H5.333c-1.51-.036-2.769-.556-3.773-1.56S.036 18.858 0 17.347v-7.36c.036-1.511.556-2.765 1.56-3.76 1.004-.996 2.262-1.52 3.773-1.574h.774l-1.174-1.12a1.234 1.234 0 0 1-.373-.906c0-.356.124-.658.373-.907l.027-.027c.267-.249.573-.373.92-.373.347 0 .653.124.92.373L9.653 4.44c.071.071.134.142.187.213h4.267a.836.836 0 0 1 .16-.213l2.853-2.747c.267-.249.573-.373.92-.373.347 0 .662.151.929.4.267.249.391.551.391.907 0 .355-.124.657-.373.906zM5.333 7.24c-.746.018-1.373.276-1.88.773-.506.498-.769 1.13-.786 1.894v7.52c.017.764.28 1.395.786 1.893.507.498 1.134.756 1.88.773h13.334c.746-.017 1.373-.275 1.88-.773.506-.498.769-1.129.786-1.893v-7.52c-.017-.765-.28-1.396-.786-1.894-.507-.497-1.134-.755-1.88-.773zM8 11.107c.373 0 .684.124.933.373.25.249.383.569.4.96v1.173c-.017.391-.15.711-.4.96-.249.25-.56.374-.933.374s-.684-.125-.933-.374c-.25-.249-.383-.569-.4-.96V12.44c0-.373.129-.689.386-.947.258-.257.574-.386.947-.386zm8 0c.373 0 .684.124.933.373.25.249.383.569.4.96v1.173c-.017.391-.15.711-.4.96-.249.25-.56.374-.933.374s-.684-.125-.933-.374c-.25-.249-.383-.569-.4-.96V12.44c.017-.391.15-.711.4-.96.249-.249.56-.373.933-.373Z",
|
|
297
|
+
linkedin:"M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z",
|
|
298
|
+
unknown:"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"
|
|
299
|
+
};
|
|
300
|
+
var d=paths[p]||paths.unknown;
|
|
301
|
+
return '<svg viewBox="0 0 24 24" width="'+s+'" height="'+s+'" fill="currentColor" style="vertical-align:-2px;flex-shrink:0;display:inline-block" aria-hidden="true"><path d="'+d+'"/></svg>';
|
|
302
|
+
}
|
|
274
303
|
|
|
275
304
|
function copyText(text){
|
|
276
305
|
navigator.clipboard.writeText(text).catch(function(){});
|
|
@@ -291,7 +320,7 @@ body{font-family:system-ui,-apple-system,'Segoe UI',sans-serif;background:var(--
|
|
|
291
320
|
+'<span style="font-size:13px;font-weight:600;color:var(--muted)">Orchyn</span></div>';
|
|
292
321
|
// Single URL tools → 1 wide card skeleton
|
|
293
322
|
if(SINGLE_URL_TOOLS[toolName]){
|
|
294
|
-
return brandHeader+'<div class="fade-in"
|
|
323
|
+
return brandHeader+'<div class="fade-in">'
|
|
295
324
|
+'<div class="skeleton-card" style="width:100%">'
|
|
296
325
|
+'<div class="skeleton skeleton-thumb" style="height:220px"></div>'
|
|
297
326
|
+'<div style="padding:14px 16px">'
|
|
@@ -313,7 +342,21 @@ body{font-family:system-ui,-apple-system,'Segoe UI',sans-serif;background:var(--
|
|
|
313
342
|
+'<div class="skeleton" style="width:140px;height:14px"></div>'
|
|
314
343
|
+'</div></div>';
|
|
315
344
|
}
|
|
316
|
-
// Gallery tools → multiple small card skeletons
|
|
345
|
+
// Gallery tools → multiple small card skeletons
|
|
346
|
+
// Unknown tool → default to 1 wide card (most common case: single URL tools)
|
|
347
|
+
if(!toolName||SINGLE_URL_TOOLS[toolName]||CREDIT_TOOLS[toolName]){
|
|
348
|
+
// Already handled above, but catch unknown tools with single card
|
|
349
|
+
if(!toolName){
|
|
350
|
+
return brandHeader+'<div class="fade-in" style="max-width:520px">'
|
|
351
|
+
+'<div class="skeleton-card" style="width:100%">'
|
|
352
|
+
+'<div class="skeleton skeleton-thumb" style="height:220px"></div>'
|
|
353
|
+
+'<div style="padding:14px 16px">'
|
|
354
|
+
+'<div class="skeleton skeleton-badge"></div>'
|
|
355
|
+
+'<div class="skeleton skeleton-line" style="width:90%"></div>'
|
|
356
|
+
+'<div class="skeleton skeleton-line medium"></div>'
|
|
357
|
+
+'</div></div></div>';
|
|
358
|
+
}
|
|
359
|
+
}
|
|
317
360
|
var count=toolName==='discover_sounds'?4:toolName==='search_creators'||toolName==='get_similar_creators'?3:6;
|
|
318
361
|
return brandHeader+'<div class="skeleton-gallery stagger">'
|
|
319
362
|
+Array(count).fill(0).map(function(){
|
|
@@ -328,7 +371,7 @@ body{font-family:system-ui,-apple-system,'Segoe UI',sans-serif;background:var(--
|
|
|
328
371
|
|
|
329
372
|
// ─── Post Card ───
|
|
330
373
|
function postCard(p,wide){
|
|
331
|
-
var platform=p.platform||"unknown",color=pColor(platform),
|
|
374
|
+
var platform=p.platform||"unknown",color=pColor(platform),brandSvg=pSvg(platform);
|
|
332
375
|
var title=p.title||p.caption||"",handle=p.creatorHandle||"",url=p.externalUrl||"";
|
|
333
376
|
var thumb=p.thumbnailUrl||"",ct=p.contentType||"post";
|
|
334
377
|
var views=p.views||0,likes=p.likes||0,comments=p.comments||0,shares=p.shares||0;
|
|
@@ -338,11 +381,42 @@ body{font-family:system-ui,-apple-system,'Segoe UI',sans-serif;background:var(--
|
|
|
338
381
|
.map(function(s){return'<span class="stat-pill">'+s[1]+" "+fmtNum(s[0])+"</span>";}).join("");
|
|
339
382
|
var handleHtml=handle?'<span class="handle">@'+esc(handle)+"</span>":"";
|
|
340
383
|
var linkHtml=url?'<a href="'+esc(url)+'" target="_blank" class="btn" style="background:'+color+'">View on '+esc(platform)+' →</a>':"";
|
|
341
|
-
|
|
384
|
+
// Prefer video playback over a static thumbnail when the post has one
|
|
385
|
+
// (videoUrl, or a video mediaItem preview_url). Videos are proxied /
|
|
386
|
+
// re-hosted to a permanent orchyn URL so they play inside the CSP.
|
|
387
|
+
var video="",images=[];
|
|
388
|
+
if(typeof p.videoUrl==="string"&&p.videoUrl)video=p.videoUrl;
|
|
389
|
+
if(p.mediaItems&&p.mediaItems.length){
|
|
390
|
+
for(var i=0;i<p.mediaItems.length;i++){
|
|
391
|
+
var m=p.mediaItems[i];
|
|
392
|
+
if(m.kind==="video"&&m.preview_url){if(!video)video=m.preview_url;}
|
|
393
|
+
else if(m.kind==="image"&&m.preview_url&&images.length<8)images.push(m.preview_url);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
var mediaHtml="";
|
|
397
|
+
var vh=wide?"340px":"200px";
|
|
398
|
+
var bodyText=p.caption||p.text||"";
|
|
399
|
+
if(video){
|
|
400
|
+
mediaHtml='<video controls preload="metadata" playsinline poster="'+esc(thumb)+'" style="width:100%;height:'+vh+';object-fit:cover;display:block;background:#000">'
|
|
401
|
+
+'Your browser doesn\'t support video playback.</video>';
|
|
402
|
+
}else if(images.length>1){
|
|
403
|
+
// Carousel / multi-image gallery — horizontally scrollable strip.
|
|
404
|
+
var strip=images.map(function(u,i){
|
|
405
|
+
return '<img src="'+esc(u)+'" alt="slide '+(i+1)+'" loading="lazy" style="height:'+vh+';max-width:85%;object-fit:cover;border-radius:6px;flex-shrink:0;display:block"/>';
|
|
406
|
+
}).join("");
|
|
407
|
+
mediaHtml='<div style="display:flex;gap:6px;overflow-x:auto;padding:8px;background:var(--border);border-bottom:1px solid var(--border)">'+strip+'</div>'
|
|
408
|
+
+'<div style="display:flex;align-items:center;gap:6px;padding:6px 10px;font-size:11px;color:var(--muted);font-weight:600;border-bottom:1px solid var(--border)">🖼 '+images.length+' images</div>';
|
|
409
|
+
}else if(thumb){
|
|
410
|
+
mediaHtml='<img src="'+esc(thumb)+'" alt="thumbnail" loading="lazy"/>';
|
|
411
|
+
}else if(bodyText){
|
|
412
|
+
// Text-only post (LinkedIn / X) — styled quote block.
|
|
413
|
+
mediaHtml='<div style="padding:16px 18px;border-bottom:1px solid var(--border);background:var(--card)">'
|
|
414
|
+
+'<div style="font-size:13px;line-height:1.55;color:var(--fg);white-space:pre-line">'+esc(bodyText.length>400?bodyText.slice(0,400)+"…":bodyText)+'</div></div>';
|
|
415
|
+
}
|
|
342
416
|
var ctTag=wide?'<span style="font-size:12px;color:var(--muted);text-transform:capitalize">'+esc(ct)+"</span>":"";
|
|
343
|
-
return '<div class="'+cls+'">'+
|
|
417
|
+
return '<div class="'+cls+'">'+mediaHtml+'<div class="card-body">'
|
|
344
418
|
+'<div style="display:flex;align-items:center;gap:6px;margin-bottom:4px">'
|
|
345
|
-
+'<span class="badge" style="background:'+color+'15;color:'+color+'">'+
|
|
419
|
+
+'<span class="badge" style="background:'+color+'15;color:'+color+';display:inline-flex;align-items:center;gap:4px">'+brandSvg+" "+esc(platform)+"</span>"
|
|
346
420
|
+ctTag+handleHtml+"</div>"
|
|
347
421
|
+'<div class="title">'+esc(title.length>160?title.slice(0,160)+"…":title)+"</div>"
|
|
348
422
|
+'<div class="stats">'+statsHtml+"</div>"
|
|
@@ -351,13 +425,13 @@ body{font-family:system-ui,-apple-system,'Segoe UI',sans-serif;background:var(--
|
|
|
351
425
|
|
|
352
426
|
// ─── Creator Card ───
|
|
353
427
|
function creatorCard(c){
|
|
354
|
-
var platform=c.platform||"tiktok",color=pColor(platform),
|
|
428
|
+
var platform=c.platform||"tiktok",color=pColor(platform),brandSvg=pSvg(platform,22);
|
|
355
429
|
var username=c.username||c.uniqueId||"",nickname=c.nickname||c.displayName||username;
|
|
356
430
|
var followers=c.followers||c.followerCount||0,sig=(c.signature||c.bio||"").slice(0,100);
|
|
357
431
|
var verified=c.verified||false,avatar=c.avatarUrl||c.avatar_thumb||"";
|
|
358
432
|
var avatarHtml=avatar
|
|
359
433
|
?'<img class="avatar" src="'+esc(avatar)+'" alt="avatar" loading="lazy"/>'
|
|
360
|
-
:'<div class="avatar-placeholder" style="background:'+color+'20">'+
|
|
434
|
+
:'<div class="avatar-placeholder" style="background:'+color+'20">'+brandSvg+"</div>";
|
|
361
435
|
var vBadge=verified?'<span class="verified" style="background:'+color+'">✓</span>':"";
|
|
362
436
|
return '<div class="creator-card">'
|
|
363
437
|
+'<div style="display:flex;align-items:center;gap:12px;margin-bottom:10px">'
|
|
@@ -494,24 +568,19 @@ body{font-family:system-ui,-apple-system,'Segoe UI',sans-serif;background:var(--
|
|
|
494
568
|
function updateProgress(params){
|
|
495
569
|
var app=document.getElementById("app");
|
|
496
570
|
if(!app)return;
|
|
497
|
-
//
|
|
498
|
-
// re-render with the correct skeleton type
|
|
571
|
+
// Learn the tool name
|
|
499
572
|
var toolName=params&¶ms.name?params.name:currentTool;
|
|
500
573
|
if(toolName&&toolName!==currentTool){
|
|
501
574
|
currentTool=toolName;
|
|
502
|
-
//
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
el.innerHTML=renderSkeleton(currentTool);
|
|
575
|
+
// Switch from spinner to correct skeleton if still loading
|
|
576
|
+
if(app.querySelector(".loading-spinner")||app.querySelector(".loading-container")||app.querySelector(".skeleton")){
|
|
577
|
+
app.innerHTML=renderSkeleton(currentTool);
|
|
506
578
|
}
|
|
507
579
|
}
|
|
508
|
-
|
|
509
|
-
app.
|
|
510
|
-
+'<div
|
|
511
|
-
|
|
512
|
-
+'<div class="loading-text">'+esc(text.length>80?text.slice(0,80)+"…":text)+'</div>'
|
|
513
|
-
+'<div class="progress-bar" style="width:200px"><div class="progress-fill" style="width:60%"></div></div>'
|
|
514
|
-
+'</div>';
|
|
580
|
+
// Add a progress bar below the existing content if not already present
|
|
581
|
+
if(!app.querySelector(".progress-bar")){
|
|
582
|
+
app.innerHTML=app.innerHTML+'<div style="text-align:center;margin-top:12px"><div class="progress-bar" style="width:200px;margin:0 auto"><div class="progress-fill" style="width:60%"></div></div></div>';
|
|
583
|
+
}
|
|
515
584
|
}
|
|
516
585
|
|
|
517
586
|
// Global helpers
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orchyn/mcp",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "MCP server for orchyn - fetch, discover and understand TikTok/Instagram/YouTube/X posts with AI (media metadata, niche discovery, hook & viral analysis)",
|
|
3
|
+
"version": "1.13.0",
|
|
4
|
+
"description": "MCP server for orchyn - fetch, discover and understand TikTok/Instagram/YouTube/X/LinkedIn posts with AI (media metadata, niche discovery, hook & viral analysis)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"files": [
|