@orchyn/mcp 1.12.2 → 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 +168 -29
- 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);
|
|
@@ -36,10 +36,13 @@ export const ORCHYN_UI_TEMPLATE = `<!DOCTYPE html>
|
|
|
36
36
|
body{font-family:system-ui,-apple-system,'Segoe UI',sans-serif;background:var(--bg);color:var(--fg);padding:16px;line-height:1.5;}
|
|
37
37
|
|
|
38
38
|
/* ─── Loading / Skeleton ─── */
|
|
39
|
-
.loading-container{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:48px 24px;gap:
|
|
40
|
-
.
|
|
39
|
+
.loading-container{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:48px 24px;gap:12px;}
|
|
40
|
+
.orchyn-logo{animation:logoSpin 3s ease-in-out infinite;}
|
|
41
|
+
@keyframes logoSpin{0%,100%{transform:rotate(0deg) scale(1)}50%{transform:rotate(180deg) scale(1.1)}}
|
|
42
|
+
.loading-brand{font-size:18px;font-weight:700;letter-spacing:-0.02em;color:var(--fg);}
|
|
43
|
+
.loading-spinner{width:28px;height:28px;border:3px solid var(--border);border-top-color:var(--accent);border-radius:50%;animation:spin .8s linear infinite;}
|
|
41
44
|
@keyframes spin{to{transform:rotate(360deg)}}
|
|
42
|
-
.loading-text{font-size:
|
|
45
|
+
.loading-text{font-size:13px;color:var(--muted);animation:pulse 1.5s ease-in-out infinite;}
|
|
43
46
|
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.5}}
|
|
44
47
|
|
|
45
48
|
.skeleton{background:var(--tag);border-radius:var(--radius-sm);overflow:hidden;position:relative;}
|
|
@@ -72,7 +75,7 @@ body{font-family:system-ui,-apple-system,'Segoe UI',sans-serif;background:var(--
|
|
|
72
75
|
.gallery{display:flex;flex-wrap:wrap;gap:10px;}
|
|
73
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;}
|
|
74
77
|
.card:hover{box-shadow:var(--shadow-md);transform:translateY(-2px);border-color:var(--muted);}
|
|
75
|
-
.card-wide{
|
|
78
|
+
.card-wide{width:100%;}
|
|
76
79
|
.card img{width:100%;height:200px;object-fit:cover;display:block;transition:transform .3s ease;}
|
|
77
80
|
.card:hover img{transform:scale(1.02);}
|
|
78
81
|
.card-wide img{max-height:340px;}
|
|
@@ -165,14 +168,30 @@ body{font-family:system-ui,-apple-system,'Segoe UI',sans-serif;background:var(--
|
|
|
165
168
|
<body>
|
|
166
169
|
<div id="app">
|
|
167
170
|
<div class="loading-container">
|
|
168
|
-
<div class="
|
|
169
|
-
|
|
171
|
+
<div class="orchyn-logo">
|
|
172
|
+
<svg width="40" height="40" viewBox="0 0 48 48" fill="none">
|
|
173
|
+
<g fill="var(--accent)" transform="translate(24 24)">
|
|
174
|
+
<circle r="4.1"/>
|
|
175
|
+
<g id="r"><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>
|
|
176
|
+
<use href="#r" transform="rotate(45)"/>
|
|
177
|
+
<use href="#r" transform="rotate(90)"/>
|
|
178
|
+
<use href="#r" transform="rotate(135)"/>
|
|
179
|
+
<use href="#r" transform="rotate(180)"/>
|
|
180
|
+
<use href="#r" transform="rotate(225)"/>
|
|
181
|
+
<use href="#r" transform="rotate(270)"/>
|
|
182
|
+
<use href="#r" transform="rotate(315)"/>
|
|
183
|
+
</g>
|
|
184
|
+
</svg>
|
|
185
|
+
</div>
|
|
186
|
+
<div class="loading-brand">Orchyn</div>
|
|
187
|
+
<div class="loading-text">Analyzing…</div>
|
|
170
188
|
</div>
|
|
171
189
|
</div>
|
|
172
190
|
<script>
|
|
173
191
|
(function(){
|
|
174
192
|
var nextId=1;var pending=new Map();
|
|
175
193
|
var loadingTimer=null;
|
|
194
|
+
var currentTool="";
|
|
176
195
|
|
|
177
196
|
function send(method,params){
|
|
178
197
|
var id=nextId++;
|
|
@@ -197,14 +216,27 @@ body{font-family:system-ui,-apple-system,'Segoe UI',sans-serif;background:var(--
|
|
|
197
216
|
render(d.params);
|
|
198
217
|
}
|
|
199
218
|
if(d.method==="ui/notifications/tool-input-partial"){
|
|
219
|
+
currentTool=d.params&&d.params.name?d.params.name:"";
|
|
200
220
|
updateProgress(d.params);
|
|
201
221
|
}
|
|
202
222
|
});
|
|
203
223
|
|
|
204
|
-
// Show
|
|
224
|
+
// Show loading state after 300ms — branded spinner until tool name arrives,
|
|
225
|
+
// then switch to the correct skeleton type.
|
|
205
226
|
loadingTimer=setTimeout(function(){
|
|
206
227
|
var app=document.getElementById("app");
|
|
207
|
-
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
|
+
}
|
|
208
240
|
},300);
|
|
209
241
|
|
|
210
242
|
// Fallback: if no tool-result arrives within 3s, check if data is embedded
|
|
@@ -228,11 +260,12 @@ body{font-family:system-ui,-apple-system,'Segoe UI',sans-serif;background:var(--
|
|
|
228
260
|
if(decoded&&typeof decoded==="object"){render(decoded);return;}
|
|
229
261
|
}
|
|
230
262
|
}catch(e){}
|
|
231
|
-
// Last resort: show a
|
|
263
|
+
// Last resort: show a branded card indicating results are available
|
|
232
264
|
var app=document.getElementById("app");
|
|
233
|
-
if(app)app.innerHTML='<div class="card card-wide fade-in"><div class="card-body">'
|
|
234
|
-
+'<
|
|
235
|
-
+'<div style="font-size:
|
|
265
|
+
if(app)app.innerHTML='<div class="card card-wide fade-in"><div class="card-body" style="text-align:center;padding:24px">'
|
|
266
|
+
+'<svg width="28" height="28" viewBox="0 0 48 48" fill="none" style="margin-bottom:8px"><g fill="var(--accent)" transform="translate(24 24)"><circle r="4.1"/><g id="rf"><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="#rf" transform="rotate(45)"/><use href="#rf" transform="rotate(90)"/><use href="#rf" transform="rotate(135)"/><use href="#rf" transform="rotate(180)"/><use href="#rf" transform="rotate(225)"/><use href="#rf" transform="rotate(270)"/><use href="#rf" transform="rotate(315)"/></g></svg>'
|
|
267
|
+
+'<div style="font-size:14px;font-weight:600;margin-bottom:4px">✅ Results ready</div>'
|
|
268
|
+
+'<div style="font-size:12px;color:var(--muted)">Orchyn analysis complete — see the full response in the chat.</div>'
|
|
236
269
|
+'</div></div>';
|
|
237
270
|
},3000);
|
|
238
271
|
|
|
@@ -248,17 +281,85 @@ body{font-family:system-ui,-apple-system,'Segoe UI',sans-serif;background:var(--
|
|
|
248
281
|
// ─── Helpers ───
|
|
249
282
|
function esc(s){return String(s||"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""");}
|
|
250
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);}
|
|
251
|
-
function pColor(p){return{tiktok:"#000",
|
|
252
|
-
|
|
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
|
+
}
|
|
253
303
|
|
|
254
304
|
function copyText(text){
|
|
255
305
|
navigator.clipboard.writeText(text).catch(function(){});
|
|
256
306
|
}
|
|
257
307
|
|
|
258
308
|
// ─── Skeleton ───
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
309
|
+
// Tools that analyze/fetch a single URL: show 1 wide card skeleton
|
|
310
|
+
var SINGLE_URL_TOOLS={analyze_post:1,get_social_media:1,understand_social_post:1,get_post_comments:1,analyze_creator_profile:1};
|
|
311
|
+
// Tools that return galleries: show multiple small card skeletons
|
|
312
|
+
var GALLERY_TOOLS={discover_social_posts:1,get_user_posts:1,search_creators:1,get_similar_creators:1,discover_sounds:1};
|
|
313
|
+
// Tools that return credits/checkout: show 1 credit card skeleton
|
|
314
|
+
var CREDIT_TOOLS={check_orchyn_credits:1,buy_orchyn_credits:1};
|
|
315
|
+
|
|
316
|
+
function renderSkeleton(toolName){
|
|
317
|
+
// Branded header above skeletons
|
|
318
|
+
var brandHeader='<div style="display:flex;align-items:center;gap:8px;margin-bottom:14px;justify-content:center">'
|
|
319
|
+
+'<svg width="20" height="20" viewBox="0 0 48 48" fill="none"><g fill="var(--accent)" transform="translate(24 24)"><circle r="4.1"/><g id="sb"><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="#sb" transform="rotate(45)"/><use href="#sb" transform="rotate(90)"/><use href="#sb" transform="rotate(135)"/><use href="#sb" transform="rotate(180)"/><use href="#sb" transform="rotate(225)"/><use href="#sb" transform="rotate(270)"/><use href="#sb" transform="rotate(315)"/></g></svg>'
|
|
320
|
+
+'<span style="font-size:13px;font-weight:600;color:var(--muted)">Orchyn</span></div>';
|
|
321
|
+
// Single URL tools → 1 wide card skeleton
|
|
322
|
+
if(SINGLE_URL_TOOLS[toolName]){
|
|
323
|
+
return brandHeader+'<div class="fade-in">'
|
|
324
|
+
+'<div class="skeleton-card" style="width:100%">'
|
|
325
|
+
+'<div class="skeleton skeleton-thumb" style="height:220px"></div>'
|
|
326
|
+
+'<div style="padding:14px 16px">'
|
|
327
|
+
+'<div class="skeleton skeleton-badge"></div>'
|
|
328
|
+
+'<div class="skeleton skeleton-line" style="width:90%"></div>'
|
|
329
|
+
+'<div class="skeleton skeleton-line medium"></div>'
|
|
330
|
+
+'<div style="display:flex;gap:6px;margin-top:10px">'
|
|
331
|
+
+'<div class="skeleton" style="width:70px;height:28px;border-radius:999px"></div>'
|
|
332
|
+
+'<div class="skeleton" style="width:60px;height:28px;border-radius:999px"></div>'
|
|
333
|
+
+'<div class="skeleton" style="width:65px;height:28px;border-radius:999px"></div>'
|
|
334
|
+
+'</div></div></div></div>';
|
|
335
|
+
}
|
|
336
|
+
// Credit tools → credit card skeleton
|
|
337
|
+
if(CREDIT_TOOLS[toolName]){
|
|
338
|
+
return brandHeader+'<div class="fade-in" style="max-width:380px">'
|
|
339
|
+
+'<div class="credits-card" style="margin:0">'
|
|
340
|
+
+'<div class="skeleton" style="width:120px;height:20px;margin-bottom:14px"></div>'
|
|
341
|
+
+'<div class="skeleton" style="width:80px;height:40px;margin-bottom:8px"></div>'
|
|
342
|
+
+'<div class="skeleton" style="width:140px;height:14px"></div>'
|
|
343
|
+
+'</div></div>';
|
|
344
|
+
}
|
|
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
|
+
}
|
|
360
|
+
var count=toolName==='discover_sounds'?4:toolName==='search_creators'||toolName==='get_similar_creators'?3:6;
|
|
361
|
+
return brandHeader+'<div class="skeleton-gallery stagger">'
|
|
362
|
+
+Array(count).fill(0).map(function(){
|
|
262
363
|
return '<div class="skeleton-card">'
|
|
263
364
|
+'<div class="skeleton skeleton-thumb"></div>'
|
|
264
365
|
+'<div class="skeleton skeleton-badge"></div>'
|
|
@@ -270,7 +371,7 @@ body{font-family:system-ui,-apple-system,'Segoe UI',sans-serif;background:var(--
|
|
|
270
371
|
|
|
271
372
|
// ─── Post Card ───
|
|
272
373
|
function postCard(p,wide){
|
|
273
|
-
var platform=p.platform||"unknown",color=pColor(platform),
|
|
374
|
+
var platform=p.platform||"unknown",color=pColor(platform),brandSvg=pSvg(platform);
|
|
274
375
|
var title=p.title||p.caption||"",handle=p.creatorHandle||"",url=p.externalUrl||"";
|
|
275
376
|
var thumb=p.thumbnailUrl||"",ct=p.contentType||"post";
|
|
276
377
|
var views=p.views||0,likes=p.likes||0,comments=p.comments||0,shares=p.shares||0;
|
|
@@ -280,11 +381,42 @@ body{font-family:system-ui,-apple-system,'Segoe UI',sans-serif;background:var(--
|
|
|
280
381
|
.map(function(s){return'<span class="stat-pill">'+s[1]+" "+fmtNum(s[0])+"</span>";}).join("");
|
|
281
382
|
var handleHtml=handle?'<span class="handle">@'+esc(handle)+"</span>":"";
|
|
282
383
|
var linkHtml=url?'<a href="'+esc(url)+'" target="_blank" class="btn" style="background:'+color+'">View on '+esc(platform)+' →</a>':"";
|
|
283
|
-
|
|
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
|
+
}
|
|
284
416
|
var ctTag=wide?'<span style="font-size:12px;color:var(--muted);text-transform:capitalize">'+esc(ct)+"</span>":"";
|
|
285
|
-
return '<div class="'+cls+'">'+
|
|
417
|
+
return '<div class="'+cls+'">'+mediaHtml+'<div class="card-body">'
|
|
286
418
|
+'<div style="display:flex;align-items:center;gap:6px;margin-bottom:4px">'
|
|
287
|
-
+'<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>"
|
|
288
420
|
+ctTag+handleHtml+"</div>"
|
|
289
421
|
+'<div class="title">'+esc(title.length>160?title.slice(0,160)+"…":title)+"</div>"
|
|
290
422
|
+'<div class="stats">'+statsHtml+"</div>"
|
|
@@ -293,13 +425,13 @@ body{font-family:system-ui,-apple-system,'Segoe UI',sans-serif;background:var(--
|
|
|
293
425
|
|
|
294
426
|
// ─── Creator Card ───
|
|
295
427
|
function creatorCard(c){
|
|
296
|
-
var platform=c.platform||"tiktok",color=pColor(platform),
|
|
428
|
+
var platform=c.platform||"tiktok",color=pColor(platform),brandSvg=pSvg(platform,22);
|
|
297
429
|
var username=c.username||c.uniqueId||"",nickname=c.nickname||c.displayName||username;
|
|
298
430
|
var followers=c.followers||c.followerCount||0,sig=(c.signature||c.bio||"").slice(0,100);
|
|
299
431
|
var verified=c.verified||false,avatar=c.avatarUrl||c.avatar_thumb||"";
|
|
300
432
|
var avatarHtml=avatar
|
|
301
433
|
?'<img class="avatar" src="'+esc(avatar)+'" alt="avatar" loading="lazy"/>'
|
|
302
|
-
:'<div class="avatar-placeholder" style="background:'+color+'20">'+
|
|
434
|
+
:'<div class="avatar-placeholder" style="background:'+color+'20">'+brandSvg+"</div>";
|
|
303
435
|
var vBadge=verified?'<span class="verified" style="background:'+color+'">✓</span>':"";
|
|
304
436
|
return '<div class="creator-card">'
|
|
305
437
|
+'<div style="display:flex;align-items:center;gap:12px;margin-bottom:10px">'
|
|
@@ -436,12 +568,19 @@ body{font-family:system-ui,-apple-system,'Segoe UI',sans-serif;background:var(--
|
|
|
436
568
|
function updateProgress(params){
|
|
437
569
|
var app=document.getElementById("app");
|
|
438
570
|
if(!app)return;
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
571
|
+
// Learn the tool name
|
|
572
|
+
var toolName=params&¶ms.name?params.name:currentTool;
|
|
573
|
+
if(toolName&&toolName!==currentTool){
|
|
574
|
+
currentTool=toolName;
|
|
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);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
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
|
+
}
|
|
445
584
|
}
|
|
446
585
|
|
|
447
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": [
|