dsh-plugin-animegarden 0.1.5 → 0.1.7
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 +19 -13
- package/dist/client.mjs +820 -4
- package/dist/index.mjs +2 -1
- package/locale/en.json +1 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -1,26 +1,32 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 🌸 Anime Garden for DeepSeek Harness
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/dsh-plugin-animegarden)
|
|
4
|
+
[](https://github.com/yjl9903/AnimeGarden/actions/workflows/ci.yml)
|
|
5
|
+
[](https://github.com/yjl9903/AnimeGarden/blob/main/LICENSE)
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
-
|
|
7
|
+
在 DeepSeek Harness 里搜索 Anime Garden 的动画 BT 资源.
|
|
8
|
+
|
|
9
|
+
- **会话搜索**:让 AI 能搜索 Anime Garden 资源
|
|
10
|
+
- **搜索面板**:输入即搜, 可按字幕组和类型筛选动画资源
|
|
11
|
+
- **新番周历**:查看当季新番动画, 快速筛选动画资源
|
|
12
|
+
- **浮窗播放**:播放链接直接在 DSH 内以浮窗打开, 边看边聊
|
|
8
13
|
|
|
9
14
|
## 安装
|
|
10
15
|
|
|
11
|
-
|
|
16
|
+
把下面这句发给你的 DeepSeek Harness:
|
|
17
|
+
|
|
18
|
+
```text
|
|
19
|
+
从 npm 安装最新版 dsh-plugin-animegarden 到当前运行的 DeepSeek Harness
|
|
20
|
+
```
|
|
12
21
|
|
|
13
|
-
|
|
22
|
+
也可以手动安装: **Plugins → Add plugin** 输入包名, 或执行 `dsh plugin add dsh-plugin-animegarden`.
|
|
14
23
|
|
|
15
|
-
|
|
16
|
-
- **搜索面板**:点击侧边栏图标打开,搜索即可;工具栏可切换周历并按字幕组 / 类型筛选。
|
|
24
|
+
安装后, 侧边栏出现 🌸 Anime Garden.
|
|
17
25
|
|
|
18
|
-
##
|
|
26
|
+
## 更新
|
|
19
27
|
|
|
20
|
-
|
|
28
|
+
暂时不支持自动更新, 先在 **插件 → 已安装插件** 中卸载当前版本, 再按上面的方式安装最新版.
|
|
21
29
|
|
|
22
30
|
## 许可证
|
|
23
31
|
|
|
24
32
|
AGPL-3.0 License © 2023 [XLor](https://github.com/yjl9903)
|
|
25
|
-
|
|
26
|
-
标题字体使用 Quicksand(SIL Open Font License 1.1)
|
package/dist/client.mjs
CHANGED
|
@@ -4339,6 +4339,29 @@ function AnimeGardenIcon({ size = 18, active = false }) {
|
|
|
4339
4339
|
);
|
|
4340
4340
|
}
|
|
4341
4341
|
|
|
4342
|
+
const playbackZh = {
|
|
4343
|
+
playbackTitle: "\u64AD\u653E\u7A97\u53E3",
|
|
4344
|
+
playbackLoading: "\u6B63\u5728\u52A0\u8F7D\u64AD\u653E\u9875\u2026",
|
|
4345
|
+
playbackReload: "\u91CD\u65B0\u52A0\u8F7D",
|
|
4346
|
+
playbackOpenExternal: "\u5728\u6D4F\u89C8\u5668\u6253\u5F00",
|
|
4347
|
+
playbackClose: "\u5173\u95ED\u64AD\u653E\u7A97\u53E3",
|
|
4348
|
+
playbackMinimize: "\u6700\u5C0F\u5316",
|
|
4349
|
+
playbackRestore: "\u8FD8\u539F\u7A97\u53E3",
|
|
4350
|
+
playbackActions: "\u64AD\u653E\u7A97\u53E3\u64CD\u4F5C",
|
|
4351
|
+
playbackMore: "\u66F4\u591A\u64CD\u4F5C"
|
|
4352
|
+
};
|
|
4353
|
+
const playbackEn = {
|
|
4354
|
+
playbackTitle: "Playback",
|
|
4355
|
+
playbackLoading: "Loading playback page\u2026",
|
|
4356
|
+
playbackReload: "Reload",
|
|
4357
|
+
playbackOpenExternal: "Open in browser",
|
|
4358
|
+
playbackClose: "Close player",
|
|
4359
|
+
playbackMinimize: "Minimize",
|
|
4360
|
+
playbackRestore: "Restore window",
|
|
4361
|
+
playbackActions: "Playback window actions",
|
|
4362
|
+
playbackMore: "More actions"
|
|
4363
|
+
};
|
|
4364
|
+
|
|
4342
4365
|
const NS = "animegarden";
|
|
4343
4366
|
const zh = {
|
|
4344
4367
|
panel: "Anime Garden",
|
|
@@ -4385,7 +4408,8 @@ const zh = {
|
|
|
4385
4408
|
size: "\u5927\u5C0F",
|
|
4386
4409
|
download: "\u4E0B\u8F7D",
|
|
4387
4410
|
copyMagnetSuccess: "\u78C1\u529B\u94FE\u63A5\u5DF2\u590D\u5236",
|
|
4388
|
-
copyMagnetFailed: "\u590D\u5236\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u590D\u5236\u78C1\u529B\u94FE\u63A5"
|
|
4411
|
+
copyMagnetFailed: "\u590D\u5236\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u590D\u5236\u78C1\u529B\u94FE\u63A5",
|
|
4412
|
+
...playbackZh
|
|
4389
4413
|
};
|
|
4390
4414
|
const en = {
|
|
4391
4415
|
panel: "Anime Garden",
|
|
@@ -4432,7 +4456,8 @@ const en = {
|
|
|
4432
4456
|
size: "Size",
|
|
4433
4457
|
download: "Download",
|
|
4434
4458
|
copyMagnetSuccess: "Magnet link copied",
|
|
4435
|
-
copyMagnetFailed: "Copy failed, copy the magnet link manually"
|
|
4459
|
+
copyMagnetFailed: "Copy failed, copy the magnet link manually",
|
|
4460
|
+
...playbackEn
|
|
4436
4461
|
};
|
|
4437
4462
|
function registerLocale(ctx) {
|
|
4438
4463
|
ctx.effect(() => ctx.locale.register(NS, { zh, en }), "animegarden: dictionaries");
|
|
@@ -4530,7 +4555,7 @@ const ErrorIcon$1 = /*#__PURE__*/ lazyReact.createElement("svg", {
|
|
|
4530
4555
|
d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-5a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-4.5A.75.75 0 0110 5zm0 10a1 1 0 100-2 1 1 0 000 2z",
|
|
4531
4556
|
clipRule: "evenodd"
|
|
4532
4557
|
}));
|
|
4533
|
-
const CloseIcon = /*#__PURE__*/ lazyReact.createElement("svg", {
|
|
4558
|
+
const CloseIcon$1 = /*#__PURE__*/ lazyReact.createElement("svg", {
|
|
4534
4559
|
xmlns: "http://www.w3.org/2000/svg",
|
|
4535
4560
|
width: "12",
|
|
4536
4561
|
height: "12",
|
|
@@ -5290,7 +5315,7 @@ const Toast = (props)=>{
|
|
|
5290
5315
|
toast.onDismiss == null ? void 0 : toast.onDismiss.call(toast, toast);
|
|
5291
5316
|
},
|
|
5292
5317
|
className: cn(classNames == null ? void 0 : classNames.closeButton, toast == null ? void 0 : (_toast_classNames2 = toast.classNames) == null ? void 0 : _toast_classNames2.closeButton)
|
|
5293
|
-
}, (_icons_close = icons == null ? void 0 : icons.close) != null ? _icons_close : CloseIcon) : null, (toastType || toast.icon || toast.promise) && toast.icon !== null && ((icons == null ? void 0 : icons[toastType]) !== null || toast.icon) ? /*#__PURE__*/ lazyReact.createElement("div", {
|
|
5318
|
+
}, (_icons_close = icons == null ? void 0 : icons.close) != null ? _icons_close : CloseIcon$1) : null, (toastType || toast.icon || toast.promise) && toast.icon !== null && ((icons == null ? void 0 : icons[toastType]) !== null || toast.icon) ? /*#__PURE__*/ lazyReact.createElement("div", {
|
|
5294
5319
|
"data-icon": "",
|
|
5295
5320
|
className: cn(classNames == null ? void 0 : classNames.icon, toast == null ? void 0 : (_toast_classNames3 = toast.classNames) == null ? void 0 : _toast_classNames3.icon)
|
|
5296
5321
|
}, toastType === 'loading' ? toast.icon || getLoadingIcon() : toast.promise ? getLoadingIcon() : null, toastType !== 'loading' ? icon : null) : null, /*#__PURE__*/ lazyReact.createElement("div", {
|
|
@@ -12491,6 +12516,7 @@ function ResourceRow({ t, resource, onRefineFansub, onCopyMagnet }) {
|
|
|
12491
12516
|
href: resource.keepshareUrl,
|
|
12492
12517
|
target: "_blank",
|
|
12493
12518
|
rel: "noreferrer noopener",
|
|
12519
|
+
"data-agp-playback-title": resource.title,
|
|
12494
12520
|
onClick: () => track("pikpak", { resource: resourceKey, source: "title" })
|
|
12495
12521
|
},
|
|
12496
12522
|
resource.title
|
|
@@ -12524,6 +12550,7 @@ function ResourceRow({ t, resource, onRefineFansub, onCopyMagnet }) {
|
|
|
12524
12550
|
rel: "noreferrer noopener",
|
|
12525
12551
|
"aria-label": t("play"),
|
|
12526
12552
|
title: t("play"),
|
|
12553
|
+
"data-agp-playback-title": resource.title,
|
|
12527
12554
|
onClick: () => track("pikpak", { resource: resourceKey, source: "play" })
|
|
12528
12555
|
},
|
|
12529
12556
|
/* @__PURE__ */ React.createElement(PlayIcon, { size: 18 })
|
|
@@ -13002,6 +13029,794 @@ function AnimeGardenPanelContent({ t: translate }) {
|
|
|
13002
13029
|
))), /* @__PURE__ */ React.createElement(Toaster, { position: "bottom-right", theme: "system" }));
|
|
13003
13030
|
}
|
|
13004
13031
|
|
|
13032
|
+
const PLAYBACK_TITLE_ATTRIBUTE = "data-agp-playback-title";
|
|
13033
|
+
const PLAYBACK_IGNORE_ATTRIBUTE = "data-agp-playback-ignore";
|
|
13034
|
+
function isKeepShareHost(hostname) {
|
|
13035
|
+
return hostname === "keepshare.cc" || hostname.endsWith(".keepshare.cc");
|
|
13036
|
+
}
|
|
13037
|
+
function parsePlaybackHref(href) {
|
|
13038
|
+
try {
|
|
13039
|
+
const url = new URL(href, window.location.href);
|
|
13040
|
+
return url.protocol === "https:" && isKeepShareHost(url.hostname) ? url.toString() : null;
|
|
13041
|
+
} catch {
|
|
13042
|
+
return null;
|
|
13043
|
+
}
|
|
13044
|
+
}
|
|
13045
|
+
const GENERIC_LABELS = /* @__PURE__ */ new Set(["\u64AD\u653E", "play", "watch", "view"]);
|
|
13046
|
+
function playbackTitle(anchor) {
|
|
13047
|
+
const stamped = (anchor.getAttribute(PLAYBACK_TITLE_ATTRIBUTE) ?? "").trim();
|
|
13048
|
+
if (stamped !== "") return stamped;
|
|
13049
|
+
const attribute = (anchor.getAttribute("title") ?? "").trim();
|
|
13050
|
+
if (attribute !== "") return attribute;
|
|
13051
|
+
const text = (anchor.textContent ?? "").replace(/\s+/g, " ").replace(/^[▶►\s]+/, "").trim();
|
|
13052
|
+
return GENERIC_LABELS.has(text.toLowerCase()) ? "" : text;
|
|
13053
|
+
}
|
|
13054
|
+
function playbackClickFromEvent(event) {
|
|
13055
|
+
if (event.defaultPrevented || event.button !== 0) return null;
|
|
13056
|
+
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return null;
|
|
13057
|
+
const node = event.target instanceof Element ? event.target : null;
|
|
13058
|
+
const anchor = node?.closest("a[href]");
|
|
13059
|
+
if (!(anchor instanceof HTMLAnchorElement)) return null;
|
|
13060
|
+
if (anchor.hasAttribute(PLAYBACK_IGNORE_ATTRIBUTE)) return null;
|
|
13061
|
+
const url = parsePlaybackHref(anchor.getAttribute("href") ?? "");
|
|
13062
|
+
if (url === null) return null;
|
|
13063
|
+
return {
|
|
13064
|
+
target: { url, title: playbackTitle(anchor) },
|
|
13065
|
+
ownLink: anchor.hasAttribute(PLAYBACK_TITLE_ATTRIBUTE)
|
|
13066
|
+
};
|
|
13067
|
+
}
|
|
13068
|
+
function installPlaybackLinkInterceptor(open) {
|
|
13069
|
+
const onClick = (event) => {
|
|
13070
|
+
const click = playbackClickFromEvent(event);
|
|
13071
|
+
if (click === null) return;
|
|
13072
|
+
event.preventDefault();
|
|
13073
|
+
if (!click.ownLink) event.stopPropagation();
|
|
13074
|
+
open(click.target);
|
|
13075
|
+
};
|
|
13076
|
+
document.addEventListener("click", onClick, true);
|
|
13077
|
+
return () => document.removeEventListener("click", onClick, true);
|
|
13078
|
+
}
|
|
13079
|
+
|
|
13080
|
+
const GEOMETRY_STORAGE_KEY = "animegarden.playback.geometry";
|
|
13081
|
+
const MIN_PLAYBACK_WIDTH = 360;
|
|
13082
|
+
const MIN_PLAYBACK_HEIGHT = 240;
|
|
13083
|
+
const MINIMIZED_HEIGHT = 40;
|
|
13084
|
+
const PLAYBACK_INSET = 8;
|
|
13085
|
+
const MINIMIZED_MARGIN = PLAYBACK_INSET * 2;
|
|
13086
|
+
function readViewportSize() {
|
|
13087
|
+
return { width: window.innerWidth, height: window.innerHeight };
|
|
13088
|
+
}
|
|
13089
|
+
function clampGeometry(geometry, viewport) {
|
|
13090
|
+
const maxWidth = Math.max(240, viewport.width - PLAYBACK_INSET * 2);
|
|
13091
|
+
const maxHeight = Math.max(160, viewport.height - PLAYBACK_INSET * 2);
|
|
13092
|
+
const width = Math.min(Math.max(MIN_PLAYBACK_WIDTH, geometry.width), maxWidth);
|
|
13093
|
+
const height = Math.min(Math.max(MIN_PLAYBACK_HEIGHT, geometry.height), maxHeight);
|
|
13094
|
+
const x = Math.min(Math.max(PLAYBACK_INSET, geometry.x), viewport.width - width - PLAYBACK_INSET);
|
|
13095
|
+
const y = Math.min(
|
|
13096
|
+
Math.max(PLAYBACK_INSET, geometry.y),
|
|
13097
|
+
viewport.height - height - PLAYBACK_INSET
|
|
13098
|
+
);
|
|
13099
|
+
return {
|
|
13100
|
+
x: Math.round(x),
|
|
13101
|
+
y: Math.round(y),
|
|
13102
|
+
width: Math.round(width),
|
|
13103
|
+
height: Math.round(height)
|
|
13104
|
+
};
|
|
13105
|
+
}
|
|
13106
|
+
function defaultGeometry(viewport) {
|
|
13107
|
+
const width = Math.round(Math.min(1080, Math.max(560, viewport.width * 0.62)));
|
|
13108
|
+
const height = Math.round(Math.min(760, Math.max(420, viewport.height * 0.72)));
|
|
13109
|
+
return clampGeometry(
|
|
13110
|
+
{
|
|
13111
|
+
x: (viewport.width - width) / 2,
|
|
13112
|
+
y: Math.max(PLAYBACK_INSET, (viewport.height - height) / 2 - 24),
|
|
13113
|
+
width,
|
|
13114
|
+
height
|
|
13115
|
+
},
|
|
13116
|
+
viewport
|
|
13117
|
+
);
|
|
13118
|
+
}
|
|
13119
|
+
function minimizedGeometry(viewport, at) {
|
|
13120
|
+
const maxWidth = Math.max(200, viewport.width - MINIMIZED_MARGIN * 2);
|
|
13121
|
+
const width = Math.round(Math.min(380, Math.max(260, viewport.width * 0.28), maxWidth));
|
|
13122
|
+
const height = MINIMIZED_HEIGHT;
|
|
13123
|
+
const maxX = Math.max(MINIMIZED_MARGIN, viewport.width - width - MINIMIZED_MARGIN);
|
|
13124
|
+
const maxY = Math.max(MINIMIZED_MARGIN, viewport.height - height - MINIMIZED_MARGIN);
|
|
13125
|
+
const x = at === null ? maxX : Math.min(Math.max(MINIMIZED_MARGIN, at.x), maxX);
|
|
13126
|
+
const y = at === null ? maxY : Math.min(Math.max(MINIMIZED_MARGIN, at.y), maxY);
|
|
13127
|
+
return { x: Math.round(x), y: Math.round(y), width, height };
|
|
13128
|
+
}
|
|
13129
|
+
function resizeGeometry(origin, direction, dx, dy, viewport) {
|
|
13130
|
+
const west = direction === "nw" || direction === "sw";
|
|
13131
|
+
const north = direction === "nw" || direction === "ne";
|
|
13132
|
+
let width;
|
|
13133
|
+
let x;
|
|
13134
|
+
if (west) {
|
|
13135
|
+
const maxWidth = Math.max(MIN_PLAYBACK_WIDTH, origin.x + origin.width - PLAYBACK_INSET);
|
|
13136
|
+
width = Math.min(Math.max(MIN_PLAYBACK_WIDTH, origin.width - dx), maxWidth);
|
|
13137
|
+
x = origin.x + origin.width - width;
|
|
13138
|
+
} else {
|
|
13139
|
+
const maxWidth = Math.max(MIN_PLAYBACK_WIDTH, viewport.width - origin.x - PLAYBACK_INSET);
|
|
13140
|
+
width = Math.min(Math.max(MIN_PLAYBACK_WIDTH, origin.width + dx), maxWidth);
|
|
13141
|
+
x = origin.x;
|
|
13142
|
+
}
|
|
13143
|
+
let height;
|
|
13144
|
+
let y;
|
|
13145
|
+
if (north) {
|
|
13146
|
+
const maxHeight = Math.max(MIN_PLAYBACK_HEIGHT, origin.y + origin.height - PLAYBACK_INSET);
|
|
13147
|
+
height = Math.min(Math.max(MIN_PLAYBACK_HEIGHT, origin.height - dy), maxHeight);
|
|
13148
|
+
y = origin.y + origin.height - height;
|
|
13149
|
+
} else {
|
|
13150
|
+
const maxHeight = Math.max(MIN_PLAYBACK_HEIGHT, viewport.height - origin.y - PLAYBACK_INSET);
|
|
13151
|
+
height = Math.min(Math.max(MIN_PLAYBACK_HEIGHT, origin.height + dy), maxHeight);
|
|
13152
|
+
y = origin.y;
|
|
13153
|
+
}
|
|
13154
|
+
return {
|
|
13155
|
+
x: Math.round(x),
|
|
13156
|
+
y: Math.round(y),
|
|
13157
|
+
width: Math.round(width),
|
|
13158
|
+
height: Math.round(height)
|
|
13159
|
+
};
|
|
13160
|
+
}
|
|
13161
|
+
function loadGeometry() {
|
|
13162
|
+
try {
|
|
13163
|
+
const raw = window.localStorage.getItem(GEOMETRY_STORAGE_KEY);
|
|
13164
|
+
if (raw === null) return null;
|
|
13165
|
+
const parsed = JSON.parse(raw);
|
|
13166
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
13167
|
+
const value = parsed;
|
|
13168
|
+
const fields = ["x", "y", "width", "height"];
|
|
13169
|
+
if (!fields.every((field) => typeof value[field] === "number")) return null;
|
|
13170
|
+
return {
|
|
13171
|
+
x: value.x,
|
|
13172
|
+
y: value.y,
|
|
13173
|
+
width: value.width,
|
|
13174
|
+
height: value.height
|
|
13175
|
+
};
|
|
13176
|
+
} catch {
|
|
13177
|
+
return null;
|
|
13178
|
+
}
|
|
13179
|
+
}
|
|
13180
|
+
function saveGeometry(geometry) {
|
|
13181
|
+
try {
|
|
13182
|
+
window.localStorage.setItem(GEOMETRY_STORAGE_KEY, JSON.stringify(geometry));
|
|
13183
|
+
} catch {
|
|
13184
|
+
}
|
|
13185
|
+
}
|
|
13186
|
+
function createPlaybackStore() {
|
|
13187
|
+
const store = new Store({
|
|
13188
|
+
target: null,
|
|
13189
|
+
geometry: loadGeometry(),
|
|
13190
|
+
minimized: false,
|
|
13191
|
+
minimizedAt: null,
|
|
13192
|
+
nonce: 0
|
|
13193
|
+
});
|
|
13194
|
+
return {
|
|
13195
|
+
store,
|
|
13196
|
+
/** Show one playback page, replacing whatever the window currently holds. */
|
|
13197
|
+
open(target) {
|
|
13198
|
+
store.setState((state) => ({
|
|
13199
|
+
...state,
|
|
13200
|
+
target,
|
|
13201
|
+
minimized: false,
|
|
13202
|
+
nonce: state.nonce + 1
|
|
13203
|
+
}));
|
|
13204
|
+
},
|
|
13205
|
+
/** Close the window; unmounting the iframe also stops playback. */
|
|
13206
|
+
close() {
|
|
13207
|
+
store.setState((state) => ({ ...state, target: null, minimized: false }));
|
|
13208
|
+
},
|
|
13209
|
+
/** Collapse to the title bar while the iframe keeps playing. */
|
|
13210
|
+
minimize() {
|
|
13211
|
+
store.setState((state) => ({ ...state, minimized: true }));
|
|
13212
|
+
},
|
|
13213
|
+
/** Expand back to the last floating rectangle. */
|
|
13214
|
+
restore() {
|
|
13215
|
+
store.setState((state) => ({ ...state, minimized: false }));
|
|
13216
|
+
},
|
|
13217
|
+
/** Remount the iframe with the same URL, matching a browser reload. */
|
|
13218
|
+
reload() {
|
|
13219
|
+
store.setState((state) => ({ ...state, target: state.target, nonce: state.nonce + 1 }));
|
|
13220
|
+
},
|
|
13221
|
+
/** Remember a user-chosen rectangle so the next open restores the placement. */
|
|
13222
|
+
commitGeometry(geometry) {
|
|
13223
|
+
const clamped = clampGeometry(geometry, readViewportSize());
|
|
13224
|
+
store.setState((state) => ({ ...state, geometry: clamped }));
|
|
13225
|
+
saveGeometry(clamped);
|
|
13226
|
+
},
|
|
13227
|
+
/** Remember where the user dragged the minimized title bar. */
|
|
13228
|
+
commitMinimizedAt(point) {
|
|
13229
|
+
const bar = minimizedGeometry(readViewportSize(), point);
|
|
13230
|
+
store.setState((state) => ({ ...state, minimizedAt: { x: bar.x, y: bar.y } }));
|
|
13231
|
+
}
|
|
13232
|
+
};
|
|
13233
|
+
}
|
|
13234
|
+
|
|
13235
|
+
const PLAYBACK_CSS = `
|
|
13236
|
+
.agp-play {
|
|
13237
|
+
position: absolute;
|
|
13238
|
+
z-index: 30;
|
|
13239
|
+
display: flex;
|
|
13240
|
+
flex-direction: column;
|
|
13241
|
+
box-sizing: border-box;
|
|
13242
|
+
overflow: hidden;
|
|
13243
|
+
color: var(--dsw-alias-label-primary);
|
|
13244
|
+
font-size: 13px;
|
|
13245
|
+
background: var(--dsw-alias-bg-overlay);
|
|
13246
|
+
border: 0.5px solid var(--dsw-alias-border-l1);
|
|
13247
|
+
border-radius: var(--dsw-radius-md, 10px);
|
|
13248
|
+
box-shadow: var(--dsw-elevation-panel);
|
|
13249
|
+
pointer-events: auto;
|
|
13250
|
+
animation: agp-play-in 140ms ease-out;
|
|
13251
|
+
}
|
|
13252
|
+
.agp-play:focus {
|
|
13253
|
+
outline: none;
|
|
13254
|
+
}
|
|
13255
|
+
.agp-play-head {
|
|
13256
|
+
display: flex;
|
|
13257
|
+
flex: none;
|
|
13258
|
+
align-items: center;
|
|
13259
|
+
gap: 8px;
|
|
13260
|
+
box-sizing: border-box;
|
|
13261
|
+
height: 40px;
|
|
13262
|
+
padding: 0 6px 0 12px;
|
|
13263
|
+
cursor: grab;
|
|
13264
|
+
user-select: none;
|
|
13265
|
+
-webkit-user-select: none;
|
|
13266
|
+
background: var(--dsw-alias-bg-layer-1);
|
|
13267
|
+
border-bottom: 0.5px solid var(--dsw-alias-border-l2);
|
|
13268
|
+
touch-action: none;
|
|
13269
|
+
}
|
|
13270
|
+
.agp-play-head-dragging {
|
|
13271
|
+
cursor: grabbing;
|
|
13272
|
+
}
|
|
13273
|
+
.agp-play-mark {
|
|
13274
|
+
flex: none;
|
|
13275
|
+
font-size: 13px;
|
|
13276
|
+
line-height: 1;
|
|
13277
|
+
}
|
|
13278
|
+
.agp-play-title {
|
|
13279
|
+
flex: 1;
|
|
13280
|
+
min-width: 0;
|
|
13281
|
+
overflow: hidden;
|
|
13282
|
+
font-size: 13px;
|
|
13283
|
+
font-weight: 500;
|
|
13284
|
+
line-height: 18px;
|
|
13285
|
+
text-overflow: ellipsis;
|
|
13286
|
+
white-space: nowrap;
|
|
13287
|
+
}
|
|
13288
|
+
.agp-play-actions {
|
|
13289
|
+
display: flex;
|
|
13290
|
+
flex: none;
|
|
13291
|
+
align-items: center;
|
|
13292
|
+
gap: 2px;
|
|
13293
|
+
}
|
|
13294
|
+
/* The overflow anchor keeps secondary actions out of the title row. */
|
|
13295
|
+
.agp-play-more {
|
|
13296
|
+
position: relative;
|
|
13297
|
+
display: inline-flex;
|
|
13298
|
+
margin-right: 6px;
|
|
13299
|
+
}
|
|
13300
|
+
.agp-play-menu {
|
|
13301
|
+
position: absolute;
|
|
13302
|
+
top: calc(100% + 4px);
|
|
13303
|
+
right: 0;
|
|
13304
|
+
z-index: 3;
|
|
13305
|
+
box-sizing: border-box;
|
|
13306
|
+
min-width: 168px;
|
|
13307
|
+
padding: 4px;
|
|
13308
|
+
background: var(--dsw-alias-bg-layer-1);
|
|
13309
|
+
border: 0.5px solid var(--dsw-alias-border-l1);
|
|
13310
|
+
border-radius: var(--dsw-radius-sm, 6px);
|
|
13311
|
+
box-shadow: var(--dsw-elevation-panel);
|
|
13312
|
+
}
|
|
13313
|
+
.agp-play-menu-item {
|
|
13314
|
+
box-sizing: border-box;
|
|
13315
|
+
display: flex;
|
|
13316
|
+
align-items: center;
|
|
13317
|
+
gap: 8px;
|
|
13318
|
+
width: 100%;
|
|
13319
|
+
height: 28px;
|
|
13320
|
+
padding: 0 8px;
|
|
13321
|
+
color: var(--dsw-alias-label-primary);
|
|
13322
|
+
font: inherit;
|
|
13323
|
+
font-size: 12px;
|
|
13324
|
+
line-height: 18px;
|
|
13325
|
+
text-align: left;
|
|
13326
|
+
text-decoration: none;
|
|
13327
|
+
white-space: nowrap;
|
|
13328
|
+
cursor: pointer;
|
|
13329
|
+
background: transparent;
|
|
13330
|
+
border: none;
|
|
13331
|
+
border-radius: var(--dsw-radius-sm, 6px);
|
|
13332
|
+
}
|
|
13333
|
+
.agp-play-menu-item:hover {
|
|
13334
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
13335
|
+
}
|
|
13336
|
+
.agp-play-menu-item:focus-visible {
|
|
13337
|
+
outline: var(--dsw-focus-ring-width, 2px) solid
|
|
13338
|
+
var(--dsw-focus-ring-color, var(--dsw-alias-brand-primary));
|
|
13339
|
+
outline-offset: -1px;
|
|
13340
|
+
}
|
|
13341
|
+
.agp-play-action {
|
|
13342
|
+
box-sizing: border-box;
|
|
13343
|
+
display: inline-flex;
|
|
13344
|
+
align-items: center;
|
|
13345
|
+
justify-content: center;
|
|
13346
|
+
width: 28px;
|
|
13347
|
+
height: 28px;
|
|
13348
|
+
padding: 0;
|
|
13349
|
+
color: var(--dsw-alias-label-secondary);
|
|
13350
|
+
font: inherit;
|
|
13351
|
+
text-decoration: none;
|
|
13352
|
+
cursor: pointer;
|
|
13353
|
+
background: transparent;
|
|
13354
|
+
border: none;
|
|
13355
|
+
border-radius: var(--dsw-radius-sm, 6px);
|
|
13356
|
+
}
|
|
13357
|
+
.agp-play-action:hover {
|
|
13358
|
+
color: var(--dsw-alias-label-primary);
|
|
13359
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
13360
|
+
}
|
|
13361
|
+
/* Close is the destructive window control: last, and tinted on hover. */
|
|
13362
|
+
.agp-play-action-close:hover {
|
|
13363
|
+
color: var(--dsw-alias-state-error-primary);
|
|
13364
|
+
}
|
|
13365
|
+
.agp-play-action:focus-visible {
|
|
13366
|
+
outline: var(--dsw-focus-ring-width, 2px) solid
|
|
13367
|
+
var(--dsw-focus-ring-color, var(--dsw-alias-brand-primary));
|
|
13368
|
+
outline-offset: 1px;
|
|
13369
|
+
}
|
|
13370
|
+
.agp-play-body {
|
|
13371
|
+
position: relative;
|
|
13372
|
+
flex: 1;
|
|
13373
|
+
min-height: 0;
|
|
13374
|
+
background: var(--dsw-alias-bg-base);
|
|
13375
|
+
}
|
|
13376
|
+
.agp-play-frame {
|
|
13377
|
+
display: block;
|
|
13378
|
+
width: 100%;
|
|
13379
|
+
height: 100%;
|
|
13380
|
+
background: var(--dsw-alias-bg-base);
|
|
13381
|
+
border: 0;
|
|
13382
|
+
}
|
|
13383
|
+
.agp-play-loading {
|
|
13384
|
+
position: absolute;
|
|
13385
|
+
inset: 0;
|
|
13386
|
+
display: flex;
|
|
13387
|
+
align-items: center;
|
|
13388
|
+
justify-content: center;
|
|
13389
|
+
background: var(--dsw-alias-bg-base);
|
|
13390
|
+
}
|
|
13391
|
+
/* Four invisible corner resize handles. The top two are L-clipped so the header's
|
|
13392
|
+
* trailing controls keep their own pointer area; the directional resize cursor is
|
|
13393
|
+
* the only affordance, so no corner marks are drawn. */
|
|
13394
|
+
.agp-play-corner {
|
|
13395
|
+
position: absolute;
|
|
13396
|
+
z-index: 2;
|
|
13397
|
+
width: 16px;
|
|
13398
|
+
height: 16px;
|
|
13399
|
+
touch-action: none;
|
|
13400
|
+
}
|
|
13401
|
+
.agp-play-corner-nw {
|
|
13402
|
+
top: 0;
|
|
13403
|
+
left: 0;
|
|
13404
|
+
cursor: nwse-resize;
|
|
13405
|
+
clip-path: polygon(0 0, 100% 0, 100% 8px, 8px 8px, 8px 100%, 0 100%);
|
|
13406
|
+
}
|
|
13407
|
+
.agp-play-corner-ne {
|
|
13408
|
+
top: 0;
|
|
13409
|
+
right: 0;
|
|
13410
|
+
cursor: nesw-resize;
|
|
13411
|
+
clip-path: polygon(0 0, 100% 0, 100% 100%, 8px 100%, 8px 8px, 0 8px);
|
|
13412
|
+
}
|
|
13413
|
+
.agp-play-corner-sw {
|
|
13414
|
+
bottom: 0;
|
|
13415
|
+
left: 0;
|
|
13416
|
+
cursor: nesw-resize;
|
|
13417
|
+
}
|
|
13418
|
+
.agp-play-corner-se {
|
|
13419
|
+
right: 0;
|
|
13420
|
+
bottom: 0;
|
|
13421
|
+
cursor: nwse-resize;
|
|
13422
|
+
}
|
|
13423
|
+
@keyframes agp-play-in {
|
|
13424
|
+
from {
|
|
13425
|
+
opacity: 0;
|
|
13426
|
+
transform: translateY(4px) scale(0.995);
|
|
13427
|
+
}
|
|
13428
|
+
to {
|
|
13429
|
+
opacity: 1;
|
|
13430
|
+
transform: none;
|
|
13431
|
+
}
|
|
13432
|
+
}
|
|
13433
|
+
/* Minimized: only the title bar is visible. The body stays mounted (so the iframe
|
|
13434
|
+
* keeps playing) and is hidden with display: none; the corner handles are unmounted
|
|
13435
|
+
* by the component and this rule is a belt-and-braces guard. */
|
|
13436
|
+
.agp-play[data-minimized='true'] .agp-play-body,
|
|
13437
|
+
.agp-play[data-minimized='true'] .agp-play-corner {
|
|
13438
|
+
display: none;
|
|
13439
|
+
}
|
|
13440
|
+
.agp-play[data-minimized='true'] .agp-play-head {
|
|
13441
|
+
border-bottom: 0;
|
|
13442
|
+
}
|
|
13443
|
+
.agp-play[data-minimized='true'] .agp-play-title {
|
|
13444
|
+
color: var(--dsw-alias-label-secondary);
|
|
13445
|
+
}
|
|
13446
|
+
@media (prefers-reduced-motion: reduce) {
|
|
13447
|
+
.agp-play {
|
|
13448
|
+
animation: none;
|
|
13449
|
+
}
|
|
13450
|
+
}
|
|
13451
|
+
`;
|
|
13452
|
+
|
|
13453
|
+
function CloseIcon({ size = 16 }) {
|
|
13454
|
+
const React = getReact();
|
|
13455
|
+
return /* @__PURE__ */ React.createElement(
|
|
13456
|
+
"svg",
|
|
13457
|
+
{
|
|
13458
|
+
viewBox: "0 0 24 24",
|
|
13459
|
+
width: size,
|
|
13460
|
+
height: size,
|
|
13461
|
+
fill: "none",
|
|
13462
|
+
"aria-hidden": "true",
|
|
13463
|
+
focusable: "false"
|
|
13464
|
+
},
|
|
13465
|
+
/* @__PURE__ */ React.createElement(
|
|
13466
|
+
"path",
|
|
13467
|
+
{
|
|
13468
|
+
d: "M6 6l12 12M18 6L6 18",
|
|
13469
|
+
stroke: "currentColor",
|
|
13470
|
+
strokeWidth: 1.8,
|
|
13471
|
+
strokeLinecap: "round"
|
|
13472
|
+
}
|
|
13473
|
+
)
|
|
13474
|
+
);
|
|
13475
|
+
}
|
|
13476
|
+
function MinimizeIcon({ size = 16 }) {
|
|
13477
|
+
const React = getReact();
|
|
13478
|
+
return /* @__PURE__ */ React.createElement(
|
|
13479
|
+
"svg",
|
|
13480
|
+
{
|
|
13481
|
+
viewBox: "0 0 24 24",
|
|
13482
|
+
width: size,
|
|
13483
|
+
height: size,
|
|
13484
|
+
fill: "none",
|
|
13485
|
+
"aria-hidden": "true",
|
|
13486
|
+
focusable: "false"
|
|
13487
|
+
},
|
|
13488
|
+
/* @__PURE__ */ React.createElement("path", { d: "M5 12h14", stroke: "currentColor", strokeWidth: 1.8, strokeLinecap: "round" })
|
|
13489
|
+
);
|
|
13490
|
+
}
|
|
13491
|
+
function RestoreIcon({ size = 16 }) {
|
|
13492
|
+
const React = getReact();
|
|
13493
|
+
return /* @__PURE__ */ React.createElement(
|
|
13494
|
+
"svg",
|
|
13495
|
+
{
|
|
13496
|
+
viewBox: "0 0 24 24",
|
|
13497
|
+
width: size,
|
|
13498
|
+
height: size,
|
|
13499
|
+
fill: "none",
|
|
13500
|
+
"aria-hidden": "true",
|
|
13501
|
+
focusable: "false"
|
|
13502
|
+
},
|
|
13503
|
+
/* @__PURE__ */ React.createElement(
|
|
13504
|
+
"path",
|
|
13505
|
+
{
|
|
13506
|
+
d: "M6 14l6-6 6 6",
|
|
13507
|
+
stroke: "currentColor",
|
|
13508
|
+
strokeWidth: 1.8,
|
|
13509
|
+
strokeLinecap: "round",
|
|
13510
|
+
strokeLinejoin: "round"
|
|
13511
|
+
}
|
|
13512
|
+
)
|
|
13513
|
+
);
|
|
13514
|
+
}
|
|
13515
|
+
function MoreIcon({ size = 16 }) {
|
|
13516
|
+
const React = getReact();
|
|
13517
|
+
return /* @__PURE__ */ React.createElement(
|
|
13518
|
+
"svg",
|
|
13519
|
+
{
|
|
13520
|
+
viewBox: "0 0 24 24",
|
|
13521
|
+
width: size,
|
|
13522
|
+
height: size,
|
|
13523
|
+
fill: "currentColor",
|
|
13524
|
+
"aria-hidden": "true",
|
|
13525
|
+
focusable: "false"
|
|
13526
|
+
},
|
|
13527
|
+
/* @__PURE__ */ React.createElement("circle", { cx: "5.5", cy: "12", r: "1.6" }),
|
|
13528
|
+
/* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "12", r: "1.6" }),
|
|
13529
|
+
/* @__PURE__ */ React.createElement("circle", { cx: "18.5", cy: "12", r: "1.6" })
|
|
13530
|
+
);
|
|
13531
|
+
}
|
|
13532
|
+
|
|
13533
|
+
function usePlaybackState(store, selector) {
|
|
13534
|
+
const React = getReact();
|
|
13535
|
+
return React.useSyncExternalStore(
|
|
13536
|
+
(onStoreChange) => {
|
|
13537
|
+
const subscription = store.store.subscribe(onStoreChange);
|
|
13538
|
+
return () => subscription.unsubscribe();
|
|
13539
|
+
},
|
|
13540
|
+
() => selector(store.store.state),
|
|
13541
|
+
() => selector(store.store.state)
|
|
13542
|
+
);
|
|
13543
|
+
}
|
|
13544
|
+
|
|
13545
|
+
const RESIZE_DIRECTIONS = ["nw", "ne", "sw", "se"];
|
|
13546
|
+
const CLICK_SLOP = 4;
|
|
13547
|
+
const INTERACTIVE_SELECTOR = 'button, a, input, select, textarea, [role="button"], [role="menu"]';
|
|
13548
|
+
function createPlaybackWindow(playback, fallbackT) {
|
|
13549
|
+
return function PlaybackWindow(props) {
|
|
13550
|
+
const React = getReact();
|
|
13551
|
+
const t = typeof props.t === "function" ? props.t : fallbackT;
|
|
13552
|
+
const target = usePlaybackState(playback, (state) => state.target);
|
|
13553
|
+
const storedGeometry = usePlaybackState(playback, (state) => state.geometry);
|
|
13554
|
+
const minimized = usePlaybackState(playback, (state) => state.minimized);
|
|
13555
|
+
const minimizedAt = usePlaybackState(playback, (state) => state.minimizedAt);
|
|
13556
|
+
const nonce = usePlaybackState(playback, (state) => state.nonce);
|
|
13557
|
+
const [viewport, setViewport] = React.useState(readViewportSize);
|
|
13558
|
+
const [dragGeometry, setDragGeometry] = React.useState(null);
|
|
13559
|
+
const [loaded, setLoaded] = React.useState(false);
|
|
13560
|
+
const [menuOpen, setMenuOpen] = React.useState(false);
|
|
13561
|
+
const drag = React.useRef(null);
|
|
13562
|
+
const root = React.useRef(null);
|
|
13563
|
+
const more = React.useRef(null);
|
|
13564
|
+
const menu = React.useRef(null);
|
|
13565
|
+
React.useEffect(() => {
|
|
13566
|
+
const onResize = () => setViewport(readViewportSize());
|
|
13567
|
+
window.addEventListener("resize", onResize);
|
|
13568
|
+
return () => window.removeEventListener("resize", onResize);
|
|
13569
|
+
}, []);
|
|
13570
|
+
React.useEffect(() => {
|
|
13571
|
+
setLoaded(false);
|
|
13572
|
+
}, [target, nonce]);
|
|
13573
|
+
React.useEffect(() => {
|
|
13574
|
+
if (target !== null) root.current?.focus();
|
|
13575
|
+
}, [target]);
|
|
13576
|
+
React.useEffect(() => {
|
|
13577
|
+
setMenuOpen(false);
|
|
13578
|
+
}, [target, nonce, minimized]);
|
|
13579
|
+
React.useEffect(() => {
|
|
13580
|
+
if (!menuOpen) return;
|
|
13581
|
+
const onPointerDown = (event) => {
|
|
13582
|
+
const node = event.target instanceof Node ? event.target : null;
|
|
13583
|
+
if (node !== null && (menu.current?.contains(node) || more.current?.contains(node))) {
|
|
13584
|
+
return;
|
|
13585
|
+
}
|
|
13586
|
+
setMenuOpen(false);
|
|
13587
|
+
};
|
|
13588
|
+
const onKeyDown = (event) => {
|
|
13589
|
+
if (event.key !== "Escape") return;
|
|
13590
|
+
event.preventDefault();
|
|
13591
|
+
event.stopPropagation();
|
|
13592
|
+
setMenuOpen(false);
|
|
13593
|
+
};
|
|
13594
|
+
document.addEventListener("pointerdown", onPointerDown, true);
|
|
13595
|
+
document.addEventListener("keydown", onKeyDown, true);
|
|
13596
|
+
return () => {
|
|
13597
|
+
document.removeEventListener("pointerdown", onPointerDown, true);
|
|
13598
|
+
document.removeEventListener("keydown", onKeyDown, true);
|
|
13599
|
+
};
|
|
13600
|
+
}, [menuOpen]);
|
|
13601
|
+
if (target === null) return null;
|
|
13602
|
+
const expandedGeometry = clampGeometry(
|
|
13603
|
+
dragGeometry ?? storedGeometry ?? defaultGeometry(viewport),
|
|
13604
|
+
viewport
|
|
13605
|
+
);
|
|
13606
|
+
const geometry = minimized ? dragGeometry === null ? minimizedGeometry(viewport, minimizedAt) : minimizedGeometry(viewport, { x: dragGeometry.x, y: dragGeometry.y }) : expandedGeometry;
|
|
13607
|
+
const title = target.title === "" ? t("playbackTitle") : target.title;
|
|
13608
|
+
const beginDrag = (mode) => (event) => {
|
|
13609
|
+
if (event.button !== 0) return;
|
|
13610
|
+
const control = event.target instanceof Element ? event.target.closest(INTERACTIVE_SELECTOR) : null;
|
|
13611
|
+
if (mode === "move" && control !== null) return;
|
|
13612
|
+
drag.current = {
|
|
13613
|
+
mode,
|
|
13614
|
+
pointerId: event.pointerId,
|
|
13615
|
+
startX: event.clientX,
|
|
13616
|
+
startY: event.clientY,
|
|
13617
|
+
origin: geometry,
|
|
13618
|
+
moved: false
|
|
13619
|
+
};
|
|
13620
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
13621
|
+
event.preventDefault();
|
|
13622
|
+
};
|
|
13623
|
+
const onPointerMove = (event) => {
|
|
13624
|
+
const session = drag.current;
|
|
13625
|
+
if (session === null || session.pointerId !== event.pointerId) return;
|
|
13626
|
+
if (minimized && session.mode !== "move") return;
|
|
13627
|
+
const dx = event.clientX - session.startX;
|
|
13628
|
+
const dy = event.clientY - session.startY;
|
|
13629
|
+
if (!session.moved && Math.abs(dx) <= CLICK_SLOP && Math.abs(dy) <= CLICK_SLOP) return;
|
|
13630
|
+
session.moved = true;
|
|
13631
|
+
if (session.mode === "move") {
|
|
13632
|
+
const next = { ...session.origin, x: session.origin.x + dx, y: session.origin.y + dy };
|
|
13633
|
+
setDragGeometry(
|
|
13634
|
+
minimized ? minimizedGeometry(viewport, next) : clampGeometry(next, viewport)
|
|
13635
|
+
);
|
|
13636
|
+
return;
|
|
13637
|
+
}
|
|
13638
|
+
setDragGeometry(resizeGeometry(session.origin, session.mode, dx, dy, viewport));
|
|
13639
|
+
};
|
|
13640
|
+
const endDrag = (event) => {
|
|
13641
|
+
const session = drag.current;
|
|
13642
|
+
if (session === null || session.pointerId !== event.pointerId) return;
|
|
13643
|
+
drag.current = null;
|
|
13644
|
+
if (event.currentTarget.hasPointerCapture(event.pointerId)) {
|
|
13645
|
+
event.currentTarget.releasePointerCapture(event.pointerId);
|
|
13646
|
+
}
|
|
13647
|
+
const barClick = minimized && session.mode === "move" && !session.moved;
|
|
13648
|
+
if (dragGeometry !== null) {
|
|
13649
|
+
if (minimized) playback.commitMinimizedAt({ x: dragGeometry.x, y: dragGeometry.y });
|
|
13650
|
+
else playback.commitGeometry(dragGeometry);
|
|
13651
|
+
}
|
|
13652
|
+
setDragGeometry(null);
|
|
13653
|
+
if (barClick) playback.restore();
|
|
13654
|
+
};
|
|
13655
|
+
return /* @__PURE__ */ React.createElement(
|
|
13656
|
+
"div",
|
|
13657
|
+
{
|
|
13658
|
+
ref: root,
|
|
13659
|
+
className: "agp-play",
|
|
13660
|
+
role: "dialog",
|
|
13661
|
+
"aria-modal": "false",
|
|
13662
|
+
"aria-label": title,
|
|
13663
|
+
tabIndex: -1,
|
|
13664
|
+
"data-minimized": minimized ? "true" : void 0,
|
|
13665
|
+
style: {
|
|
13666
|
+
left: `${geometry.x}px`,
|
|
13667
|
+
top: `${geometry.y}px`,
|
|
13668
|
+
width: `${geometry.width}px`,
|
|
13669
|
+
height: `${geometry.height}px`
|
|
13670
|
+
},
|
|
13671
|
+
onKeyDown: (event) => {
|
|
13672
|
+
if (event.key !== "Escape") return;
|
|
13673
|
+
event.stopPropagation();
|
|
13674
|
+
playback.close();
|
|
13675
|
+
}
|
|
13676
|
+
},
|
|
13677
|
+
/* @__PURE__ */ React.createElement("style", null, PLAYBACK_CSS),
|
|
13678
|
+
/* @__PURE__ */ React.createElement(
|
|
13679
|
+
"header",
|
|
13680
|
+
{
|
|
13681
|
+
className: dragGeometry === null ? "agp-play-head" : "agp-play-head agp-play-head-dragging",
|
|
13682
|
+
onPointerDown: beginDrag("move"),
|
|
13683
|
+
onPointerMove,
|
|
13684
|
+
onPointerUp: endDrag,
|
|
13685
|
+
onPointerCancel: endDrag
|
|
13686
|
+
},
|
|
13687
|
+
/* @__PURE__ */ React.createElement("span", { className: "agp-play-mark", "aria-hidden": "true" }, "\u{1F338}"),
|
|
13688
|
+
/* @__PURE__ */ React.createElement("span", { className: "agp-play-title", title }, title),
|
|
13689
|
+
/* @__PURE__ */ React.createElement("div", { className: "agp-play-actions", role: "toolbar", "aria-label": t("playbackActions") }, minimized ? null : /* @__PURE__ */ React.createElement("div", { className: "agp-play-more", ref: more }, /* @__PURE__ */ React.createElement(
|
|
13690
|
+
"button",
|
|
13691
|
+
{
|
|
13692
|
+
type: "button",
|
|
13693
|
+
className: "agp-play-action",
|
|
13694
|
+
"aria-label": t("playbackMore"),
|
|
13695
|
+
title: t("playbackMore"),
|
|
13696
|
+
"aria-haspopup": "menu",
|
|
13697
|
+
"aria-expanded": menuOpen,
|
|
13698
|
+
onClick: () => setMenuOpen((open) => !open)
|
|
13699
|
+
},
|
|
13700
|
+
/* @__PURE__ */ React.createElement(MoreIcon, null)
|
|
13701
|
+
), menuOpen ? /* @__PURE__ */ React.createElement(
|
|
13702
|
+
"div",
|
|
13703
|
+
{
|
|
13704
|
+
className: "agp-play-menu",
|
|
13705
|
+
role: "menu",
|
|
13706
|
+
"aria-label": t("playbackActions"),
|
|
13707
|
+
ref: menu
|
|
13708
|
+
},
|
|
13709
|
+
/* @__PURE__ */ React.createElement(
|
|
13710
|
+
"button",
|
|
13711
|
+
{
|
|
13712
|
+
type: "button",
|
|
13713
|
+
role: "menuitem",
|
|
13714
|
+
className: "agp-play-menu-item",
|
|
13715
|
+
onClick: () => {
|
|
13716
|
+
setMenuOpen(false);
|
|
13717
|
+
playback.reload();
|
|
13718
|
+
}
|
|
13719
|
+
},
|
|
13720
|
+
/* @__PURE__ */ React.createElement(RefreshIcon, null),
|
|
13721
|
+
/* @__PURE__ */ React.createElement("span", null, t("playbackReload"))
|
|
13722
|
+
),
|
|
13723
|
+
/* @__PURE__ */ React.createElement(
|
|
13724
|
+
"a",
|
|
13725
|
+
{
|
|
13726
|
+
role: "menuitem",
|
|
13727
|
+
className: "agp-play-menu-item",
|
|
13728
|
+
href: target.url,
|
|
13729
|
+
target: "_blank",
|
|
13730
|
+
rel: "noreferrer noopener",
|
|
13731
|
+
"data-agp-playback-ignore": "",
|
|
13732
|
+
onClick: () => setMenuOpen(false)
|
|
13733
|
+
},
|
|
13734
|
+
/* @__PURE__ */ React.createElement(LaunchIcon, { size: 14 }),
|
|
13735
|
+
/* @__PURE__ */ React.createElement("span", null, t("playbackOpenExternal"))
|
|
13736
|
+
)
|
|
13737
|
+
) : null), minimized ? /* @__PURE__ */ React.createElement(
|
|
13738
|
+
"button",
|
|
13739
|
+
{
|
|
13740
|
+
type: "button",
|
|
13741
|
+
className: "agp-play-action",
|
|
13742
|
+
"aria-label": t("playbackRestore"),
|
|
13743
|
+
title: t("playbackRestore"),
|
|
13744
|
+
onClick: () => playback.restore()
|
|
13745
|
+
},
|
|
13746
|
+
/* @__PURE__ */ React.createElement(RestoreIcon, null)
|
|
13747
|
+
) : /* @__PURE__ */ React.createElement(
|
|
13748
|
+
"button",
|
|
13749
|
+
{
|
|
13750
|
+
type: "button",
|
|
13751
|
+
className: "agp-play-action",
|
|
13752
|
+
"aria-label": t("playbackMinimize"),
|
|
13753
|
+
title: t("playbackMinimize"),
|
|
13754
|
+
onClick: () => playback.minimize()
|
|
13755
|
+
},
|
|
13756
|
+
/* @__PURE__ */ React.createElement(MinimizeIcon, null)
|
|
13757
|
+
), /* @__PURE__ */ React.createElement(
|
|
13758
|
+
"button",
|
|
13759
|
+
{
|
|
13760
|
+
type: "button",
|
|
13761
|
+
className: "agp-play-action agp-play-action-close",
|
|
13762
|
+
"aria-label": t("playbackClose"),
|
|
13763
|
+
title: t("playbackClose"),
|
|
13764
|
+
onClick: () => playback.close()
|
|
13765
|
+
},
|
|
13766
|
+
/* @__PURE__ */ React.createElement(CloseIcon, null)
|
|
13767
|
+
))
|
|
13768
|
+
),
|
|
13769
|
+
/* @__PURE__ */ React.createElement("div", { className: "agp-play-body" }, /* @__PURE__ */ React.createElement(
|
|
13770
|
+
"iframe",
|
|
13771
|
+
{
|
|
13772
|
+
key: `${target.url}#${String(nonce)}`,
|
|
13773
|
+
className: "agp-play-frame",
|
|
13774
|
+
src: target.url,
|
|
13775
|
+
title,
|
|
13776
|
+
allow: "autoplay; fullscreen; picture-in-picture; clipboard-read; clipboard-write; encrypted-media",
|
|
13777
|
+
allowFullScreen: true,
|
|
13778
|
+
referrerPolicy: "no-referrer",
|
|
13779
|
+
onLoad: () => setLoaded(true)
|
|
13780
|
+
}
|
|
13781
|
+
), loaded ? null : /* @__PURE__ */ React.createElement("div", { className: "agp-play-loading" }, /* @__PURE__ */ React.createElement(LoadingStatus, { label: t("playbackLoading") }))),
|
|
13782
|
+
minimized ? null : RESIZE_DIRECTIONS.map((direction) => /* @__PURE__ */ React.createElement(
|
|
13783
|
+
"div",
|
|
13784
|
+
{
|
|
13785
|
+
key: direction,
|
|
13786
|
+
className: `agp-play-corner agp-play-corner-${direction}`,
|
|
13787
|
+
"aria-hidden": "true",
|
|
13788
|
+
onPointerDown: beginDrag(direction),
|
|
13789
|
+
onPointerMove,
|
|
13790
|
+
onPointerUp: endDrag,
|
|
13791
|
+
onPointerCancel: endDrag
|
|
13792
|
+
}
|
|
13793
|
+
))
|
|
13794
|
+
);
|
|
13795
|
+
};
|
|
13796
|
+
}
|
|
13797
|
+
|
|
13798
|
+
const PLAYBACK_SLOT_ID = "animegarden.playback";
|
|
13799
|
+
function registerPlaybackFeature(ctx, t) {
|
|
13800
|
+
const playback = createPlaybackStore();
|
|
13801
|
+
const PlaybackWindow = createPlaybackWindow(playback, t);
|
|
13802
|
+
ctx.effect(
|
|
13803
|
+
() => installPlaybackLinkInterceptor((target) => playback.open(target)),
|
|
13804
|
+
"animegarden: playback link interceptor"
|
|
13805
|
+
);
|
|
13806
|
+
ctx.slots.inject(
|
|
13807
|
+
"shell.overlay",
|
|
13808
|
+
() => ctx.slots.register(
|
|
13809
|
+
{
|
|
13810
|
+
name: "shell.overlay",
|
|
13811
|
+
id: PLAYBACK_SLOT_ID,
|
|
13812
|
+
order: 30,
|
|
13813
|
+
locale: NS
|
|
13814
|
+
},
|
|
13815
|
+
PlaybackWindow
|
|
13816
|
+
)
|
|
13817
|
+
);
|
|
13818
|
+
}
|
|
13819
|
+
|
|
13005
13820
|
const PANEL_ID = "animegarden";
|
|
13006
13821
|
function createPlugin(ReactRuntime) {
|
|
13007
13822
|
setReact(ReactRuntime);
|
|
@@ -13019,6 +13834,7 @@ function createPlugin(ReactRuntime) {
|
|
|
13019
13834
|
inject: ["slots", "locale"],
|
|
13020
13835
|
apply(ctx) {
|
|
13021
13836
|
const t = registerLocale(ctx);
|
|
13837
|
+
registerPlaybackFeature(ctx, t);
|
|
13022
13838
|
ctx.slots.inject(
|
|
13023
13839
|
"sidebar.panellist",
|
|
13024
13840
|
() => ctx.slots.register(
|
package/dist/index.mjs
CHANGED
|
@@ -69,13 +69,14 @@ function renderMarkdown(args, value) {
|
|
|
69
69
|
const lines = ["### AnimeGarden \u8D44\u6E90\u641C\u7D22", ""];
|
|
70
70
|
if (resources.length === 0) {
|
|
71
71
|
lines.push(
|
|
72
|
-
search.length > 0 ? `\u672A\u627E\u5230\u5339\u914D\u300C${search}\u300D\u7684\u8D44\u6E90\uFF08\u6700\u591A ${limit} \u6761\uFF09\u3002
|
|
72
|
+
search.length > 0 ? `\u672A\u627E\u5230\u5339\u914D\u300C${search}\u300D\u7684\u8D44\u6E90\uFF08\u6700\u591A ${limit} \u6761\uFF09\u3002` : `\u672A\u627E\u5230\u5339\u914D\u8D44\u6E90\uFF08\u6700\u591A ${limit} \u6761\uFF09\u3002`
|
|
73
73
|
);
|
|
74
74
|
lines.push("");
|
|
75
75
|
lines.push(`\u5206\u9875\u53C2\u6570\uFF1A${formatPagination(value.pagination)}`);
|
|
76
76
|
lines.push(`\u7B5B\u9009\u53C2\u6570\uFF1A${formatResolvedFilter(value.filter)}`);
|
|
77
77
|
lines.push(`\u54CD\u5E94\u65F6\u95F4\uFF1A\`${value.timestamp}\``);
|
|
78
78
|
lines.push("");
|
|
79
|
+
lines.push("\u53EF\u5C1D\u8BD5\u653E\u5BBD\u7B5B\u9009\uFF1A");
|
|
79
80
|
lines.push(...emptyResultHints(input).map((hint) => `- ${hint}`));
|
|
80
81
|
return [{ type: "text", text: lines.join("\n").trim() }];
|
|
81
82
|
}
|
package/locale/en.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"meta": {
|
|
3
3
|
"title": "Anime Garden",
|
|
4
|
-
"description": "
|
|
4
|
+
"description": "Anime Garden is an anime torrent aggregator that lets you search for anime resources from 動漫花園, 蜜柑计划, 萌番组, and ANi in one place."
|
|
5
5
|
}
|
|
6
6
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-animegarden",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Anime Garden 是一个动画 BT 资源聚合站, 你可以在这里聚合搜索来自動漫花園、蜜柑计划、萌番组和 ANi 的动画资源.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"anime",
|
|
@@ -50,12 +50,14 @@
|
|
|
50
50
|
"sonner": "2.0.8"
|
|
51
51
|
},
|
|
52
52
|
"engines": {
|
|
53
|
-
"node": ">=24
|
|
53
|
+
"node": ">=24",
|
|
54
|
+
"dsh": ">=0.2.1-rc.5"
|
|
54
55
|
},
|
|
55
56
|
"publishConfig": {
|
|
56
57
|
"access": "public"
|
|
57
58
|
},
|
|
58
59
|
"dsh": {
|
|
60
|
+
"manifestVersion": 1,
|
|
59
61
|
"bundle": {
|
|
60
62
|
"patch": "./cordis.patch.yml"
|
|
61
63
|
},
|