@leotang92/dsh-claude-motion 0.0.0-stage → 0.1.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.
Files changed (33) hide show
  1. package/LICENSE +27 -0
  2. package/NOTICE.md +25 -0
  3. package/README.md +21 -2
  4. package/cordis.patch.yml +7 -0
  5. package/docs/DETAIL.md +32 -0
  6. package/docs/DETAIL.zh.md +17 -0
  7. package/icon.svg +8 -0
  8. package/lib/client.js +1800 -0
  9. package/lib/index.js +878 -0
  10. package/locale/en.json +7 -0
  11. package/locale/zh.json +7 -0
  12. package/package.json +69 -4
  13. package/skills/claude-motion/SKILL.md +689 -0
  14. package/skills/claude-motion/artifact-type/reference/by-hand.md +84 -0
  15. package/skills/claude-motion/artifact-type/reference/film-api.md +278 -0
  16. package/skills/claude-motion/artifact-type/reference/finalize.md +213 -0
  17. package/skills/claude-motion/artifact-type/reference/motion.md +260 -0
  18. package/skills/claude-motion/artifact-type/reference/plan.md +239 -0
  19. package/skills/claude-motion/artifact-type/reference/questions.md +240 -0
  20. package/skills/claude-motion/artifact-type/reference/recreate-ui.md +140 -0
  21. package/skills/claude-motion/artifact-type/reference/render-offline.md +126 -0
  22. package/skills/claude-motion/artifact-type/reference/rough-cut.md +251 -0
  23. package/skills/claude-motion/artifact-type/reference/sound.md +106 -0
  24. package/skills/claude-motion/artifact-type/reference/styles.md +230 -0
  25. package/skills/claude-motion/artifact-type/reference/walkthrough.md +83 -0
  26. package/skills/claude-motion/scripts/artifact.mjs +8390 -0
  27. package/skills/claude-motion/scripts/editor/artifact-type/anthropicons-fizzy-lake-DtzHkSJH.woff2 +0 -0
  28. package/skills/claude-motion/scripts/editor/artifact-type/app.css +1 -0
  29. package/skills/claude-motion/scripts/editor/artifact-type/app.js +17230 -0
  30. package/skills/claude-motion/scripts/editor/index.html +145 -0
  31. package/skills/claude-motion/scripts/editor/local-host.js +12 -0
  32. package/skills/claude-motion/scripts/editor/share-ui.js +208 -0
  33. package/skills/claude-motion/scripts/editor/third-party-licenses.txt +970 -0
package/lib/client.js ADDED
@@ -0,0 +1,1800 @@
1
+ // @leotang92/dsh-claude-motion 0.1.0: packaged from tools/, host adapted from baoyu-design.
2
+ window.__ModuleLoader__.load({
3
+ id: "@leotang92/dsh-claude-motion",
4
+ factory: (require) => {
5
+ var module = { exports: {} };
6
+ var exports = module.exports;
7
+ "use strict";
8
+ var __defProp = Object.defineProperty;
9
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
10
+ var __getOwnPropNames = Object.getOwnPropertyNames;
11
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
12
+ var __export = (target, all) => {
13
+ for (var name in all)
14
+ __defProp(target, name, { get: all[name], enumerable: true });
15
+ };
16
+ var __copyProps = (to, from, except, desc) => {
17
+ if (from && typeof from === "object" || typeof from === "function") {
18
+ for (let key of __getOwnPropNames(from))
19
+ if (!__hasOwnProp.call(to, key) && key !== except)
20
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
21
+ }
22
+ return to;
23
+ };
24
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
25
+
26
+ var client_exports = {};
27
+ __export(client_exports, {
28
+ apply: () => apply,
29
+ inject: () => inject
30
+ });
31
+ module.exports = __toCommonJS(client_exports);
32
+
33
+ var define_CLIENT_default = { plugin: "@leotang92/dsh-claude-motion", skill: "claude-motion", key: "@leotang92/dsh-claude-motion", simple: false };
34
+
35
+ var import_react2 = require("react");
36
+
37
+ var import_react = require("react");
38
+
39
+ var base = (skill) => `/api/baoyu-design.${skill}.artifacts`;
40
+ var listPath = base;
41
+ var openPath = (skill) => `${base(skill)}.open`;
42
+ var previewPath = (skill) => `${base(skill)}.preview`;
43
+ var releasePath = (skill) => `${base(skill)}.release`;
44
+ var thumbnailPath = (skill) => `${base(skill)}.thumbnail`;
45
+ var updatePath = (skill) => `${base(skill)}.update`;
46
+ var deletePath = (skill) => `${base(skill)}.delete`;
47
+ var restorePath = (skill) => `${base(skill)}.restore`;
48
+
49
+ function desktopBrowser() {
50
+ const carrier = globalThis.dshDesktop;
51
+ return carrier?.protocolVersion === 1 ? carrier.browser : void 0;
52
+ }
53
+ var WORKSPACE = "baoyu-design-thumbnails";
54
+ var WIDTH = 720;
55
+ var QUALITY = 0.82;
56
+ var LOAD_MS = 2e4;
57
+ var SETTLE_MS = 1800;
58
+ var BLANK_RETRY_MS = 3e3;
59
+ var REGIONS = {
60
+ // The current slide on the editor's canvas.
61
+ "claude-slides": { width: 1280, height: 800, find: `document.querySelector('.bg-editor-canvas > .shadow-lg')` },
62
+ // The first artboard.
63
+ "claude-design": { width: 1280, height: 800, find: `document.querySelector('[data-mega-artboard]')` },
64
+ // What is beside the section list: the system's cover.
65
+ "claude-design-system": {
66
+ width: 1280,
67
+ height: 800,
68
+ find: `(() => {
69
+ const nav = document.querySelector('[data-testid="ds-section-nav"]');
70
+ if (!nav || !document.querySelector('h1,h2')) return null;
71
+ const r = nav.getBoundingClientRect();
72
+ return { x: r.right, y: r.top, width: innerWidth - r.right, height: innerHeight - r.top };
73
+ })()`
74
+ },
75
+ // The page, below the bar.
76
+ "claude-dashboard": { width: 1280, height: 800, find: `document.querySelector('main')` },
77
+ // The doc's frame, below the bars, at a width where the text fills it.
78
+ "claude-docs": { width: 860, height: 700, find: `document.querySelector('iframe')` },
79
+ // The film's first frame.
80
+ "claude-motion": { width: 1280, height: 800, find: `document.querySelector('.an-stage iframe')` }
81
+ };
82
+ var sleep = (ms, signal) => new Promise((resolve, reject) => {
83
+ if (signal.aborted) return reject(signal.reason);
84
+ const timer = setTimeout(resolve, ms);
85
+ signal.addEventListener("abort", () => {
86
+ clearTimeout(timer);
87
+ reject(signal.reason);
88
+ }, { once: true });
89
+ });
90
+ async function toCanvas(image) {
91
+ const picture = new Image();
92
+ await new Promise((resolve, reject) => {
93
+ picture.onload = () => resolve();
94
+ picture.onerror = () => reject(new Error("the picture did not decode"));
95
+ picture.src = image.toDataURL();
96
+ });
97
+ const scale = Math.min(1, WIDTH / picture.naturalWidth);
98
+ const canvas = document.createElement("canvas");
99
+ canvas.width = Math.max(1, Math.round(picture.naturalWidth * scale));
100
+ canvas.height = Math.max(1, Math.round(picture.naturalHeight * scale));
101
+ const context = canvas.getContext("2d");
102
+ if (context === null) throw new Error("no canvas to draw on");
103
+ context.imageSmoothingQuality = "high";
104
+ context.drawImage(picture, 0, 0, canvas.width, canvas.height);
105
+ return canvas;
106
+ }
107
+ function isFlat(canvas) {
108
+ const { data } = canvas.getContext("2d").getImageData(0, 0, canvas.width, canvas.height);
109
+ const step = Math.max(4, Math.floor(data.length / 4 / 400) * 4);
110
+ for (let i = step; i + 2 < data.length; i += step) {
111
+ if (Math.abs(data[i] - data[0]) > 3 || Math.abs(data[i + 1] - data[1]) > 3 || Math.abs(data[i + 2] - data[2]) > 3) return false;
112
+ }
113
+ return true;
114
+ }
115
+ async function captureThumbnail(browser, url, skill, signal) {
116
+ const region = REGIONS[skill] ?? { width: 1280, height: 800, find: "null" };
117
+ const { lease, partition } = await browser.acquire(WORKSPACE);
118
+ const guest = document.createElement("webview");
119
+ try {
120
+ guest.setAttribute("name", lease);
121
+ guest.setAttribute("partition", partition);
122
+ guest.setAttribute("src", `about:blank#${lease}`);
123
+ guest.setAttribute("aria-hidden", "true");
124
+ guest.tabIndex = -1;
125
+ guest.style.cssText = `position:fixed;left:calc(100vw - 1px);top:0;width:${region.width}px;height:${region.height}px;opacity:.01;pointer-events:none;z-index:-1`;
126
+ const ready = new Promise((resolve) => guest.addEventListener("dom-ready", () => resolve(), { once: true }));
127
+ document.body.append(guest);
128
+ await Promise.race([ready, sleep(LOAD_MS, signal)]);
129
+ guest.setAudioMuted(true);
130
+ const loaded = new Promise((resolve) => guest.addEventListener("did-stop-loading", () => resolve(), { once: true }));
131
+ void guest.loadURL(url).catch(() => void 0);
132
+ await Promise.race([loaded, sleep(LOAD_MS, signal)]);
133
+ const measure = `(() => {
134
+ const found = ${region.find};
135
+ if (!found) return null;
136
+ const r = found.getBoundingClientRect ? found.getBoundingClientRect() : found;
137
+ if (r.width < 80 || r.height < 60) return null;
138
+ const x = Math.max(0, r.x), y = Math.max(0, r.y);
139
+ return { x, y, width: Math.min(r.width, innerWidth - x), height: Math.min(r.height, innerHeight - y) };
140
+ })()`;
141
+ let rect = null;
142
+ const deadline = Date.now() + LOAD_MS;
143
+ while (rect === null && Date.now() < deadline) {
144
+ rect = await guest.executeJavaScript(measure).catch(() => null);
145
+ if (rect === null) await sleep(250, signal);
146
+ }
147
+ await guest.insertCSS("::-webkit-scrollbar{display:none!important}").catch(() => "");
148
+ await guest.executeJavaScript("document.fonts.ready.then(() => true)").catch(() => null);
149
+ await sleep(SETTLE_MS, signal);
150
+ rect = await guest.executeJavaScript(measure).catch(() => null) ?? rect;
151
+ const area = rect && {
152
+ x: Math.round(rect.x),
153
+ y: Math.round(rect.y),
154
+ width: Math.round(rect.width),
155
+ height: Math.round(rect.height)
156
+ };
157
+ const take = async () => {
158
+ const image = await guest.capturePage(area ?? void 0);
159
+ if (image.isEmpty()) throw new Error("the page gave no picture");
160
+ return toCanvas(image);
161
+ };
162
+ let canvas = await take();
163
+ if (isFlat(canvas)) {
164
+ await sleep(BLANK_RETRY_MS, signal);
165
+ canvas = await take();
166
+ }
167
+ const blob = await new Promise((resolve) => canvas.toBlob(resolve, "image/jpeg", QUALITY));
168
+ if (blob === null) throw new Error("the picture did not encode");
169
+ return new Uint8Array(await blob.arrayBuffer());
170
+ } finally {
171
+ guest.remove();
172
+ await browser.release(lease).catch(() => void 0);
173
+ }
174
+ }
175
+
176
+ var import_jsx_runtime = require("react/jsx-runtime");
177
+ var PAGE_VERSION = 2;
178
+ var PANEL_ID = "baoyu-design.artifacts";
179
+ var NS = "baoyu-design-artifacts";
180
+ var VIEW_KEY = "baoyu-design.artifacts.view";
181
+ var HUB = Symbol.for("baoyu-design/dsh-artifacts-page");
182
+ function hub() {
183
+ const g = globalThis;
184
+ return g[HUB] ??= { members: /* @__PURE__ */ new Map(), owner: null, listeners: /* @__PURE__ */ new Set() };
185
+ }
186
+ function newest(h) {
187
+ let best;
188
+ for (const m of h.members.values()) if (best === void 0 || m.version > best.version) best = m;
189
+ return best;
190
+ }
191
+ function settle(h) {
192
+ const next = newest(h);
193
+ if (h.owner !== null && h.members.has(h.owner.plugin) && (next === void 0 || next.version <= h.owner.version)) return;
194
+ if (h.owner !== null) {
195
+ const old = h.owner;
196
+ h.owner = null;
197
+ old.dispose();
198
+ }
199
+ if (next === void 0) return;
200
+ try {
201
+ h.owner = { plugin: next.plugin, version: next.version, dispose: next.mount() };
202
+ } catch (error) {
203
+ console.error(`${next.plugin}: the Artifacts page did not register:`, error);
204
+ }
205
+ }
206
+ function changed(h) {
207
+ for (const listener of [...h.listeners]) {
208
+ try {
209
+ listener();
210
+ } catch (error) {
211
+ console.error("baoyu-design Artifacts page listener failed:", error);
212
+ }
213
+ }
214
+ }
215
+ function createStore(initial) {
216
+ let state = initial;
217
+ const listeners = /* @__PURE__ */ new Set();
218
+ return {
219
+ getSnapshot: () => state,
220
+ subscribe(listener) {
221
+ listeners.add(listener);
222
+ return () => listeners.delete(listener);
223
+ },
224
+ set(patch) {
225
+ state = { ...state, ...patch };
226
+ for (const listener of [...listeners]) listener();
227
+ }
228
+ };
229
+ }
230
+ var EMPTY_SESSIONS = { getSnapshot: () => ({}), subscribe: () => () => {
231
+ } };
232
+ function isStore(value) {
233
+ const v = value;
234
+ return typeof v?.getSnapshot === "function" && typeof v.subscribe === "function";
235
+ }
236
+ function linkState(id, sessions, workspaces) {
237
+ if (sessions === void 0) return "unavailable";
238
+ if (workspaces?.state === "error") return "unavailable";
239
+ if (sessions.phase === "pending" || workspaces?.phase === "pending") return "loading";
240
+ if (workspaces?.archivedSessionIds?.includes(id)) return "archived";
241
+ if (!sessions.ids?.includes(id)) return "unavailable";
242
+ return "available";
243
+ }
244
+ var zh = {
245
+ panel: "作品",
246
+ title: "作品",
247
+ "time.locale": "zh-CN",
248
+ search: "搜索作品",
249
+ "search.clear": "清除搜索",
250
+ "type.claude-slides": "幻灯片",
251
+ "type.claude-design": "设计",
252
+ "type.claude-design-system": "设计系统",
253
+ "type.claude-dashboard": "看板",
254
+ "type.claude-docs": "文档",
255
+ "type.claude-motion": "动画",
256
+ "create.heading": "新建",
257
+ "create.new": "新建",
258
+ "create.session": "新会话",
259
+ "prompt.claude-slides": "帮我做一份幻灯片,主题是:",
260
+ "prompt.claude-design": "帮我设计:",
261
+ "prompt.claude-design-system": "帮我做一套设计系统,品牌是:",
262
+ "prompt.claude-dashboard": "帮我做一个数据看板,数据是:",
263
+ "prompt.claude-docs": "帮我写一份文档,内容是:",
264
+ "prompt.claude-motion": "帮我做一段动画,内容是:",
265
+ "view.list": "列表视图",
266
+ "view.grid": "网格视图",
267
+ "filter.button": "排序和筛选",
268
+ "filter.sort": "排序方式",
269
+ "filter.type": "类型",
270
+ "filter.date": "打开时间",
271
+ "filter.sharing": "共享",
272
+ "filter.pinned": "已固定",
273
+ "filter.any": "不限",
274
+ "filter.some": "{count} 项",
275
+ "filter.clear": "清除筛选",
276
+ "sort.opened": "最近打开",
277
+ "sort.updated": "最近修改",
278
+ "sort.created": "创建时间",
279
+ "sort.name": "名称",
280
+ "date.any": "不限",
281
+ "date.today": "今天",
282
+ "date.yesterday": "昨天",
283
+ "date.week": "最近 7 天",
284
+ "date.month": "最近 30 天",
285
+ "sharing.any": "不限",
286
+ "sharing.shared": "已共享",
287
+ "sharing.private": "未共享",
288
+ "card.opened": "{time}打开",
289
+ "card.justNow": "刚刚打开",
290
+ "card.opening": "正在打开…",
291
+ "card.shared": "已在局域网共享",
292
+ "action.more": "更多操作",
293
+ "action.pin": "固定",
294
+ "action.unpin": "取消固定",
295
+ "action.rename": "重命名",
296
+ "action.copy": "复制链接",
297
+ "action.delete": "删除",
298
+ "rename.title": "重命名作品",
299
+ "rename.save": "保存",
300
+ "dialog.cancel": "取消",
301
+ "delete.title": "删除“{title}”?",
302
+ "delete.moves": "这会移除这个作品,它的文件夹移到应用数据目录下的 baoyu-design/trash。",
303
+ "delete.keeps": "这会把它从作品里移除。它的文件夹里还有别的文件,所以留在原处:{path}",
304
+ "delete.confirm": "删除",
305
+ "toast.copied": "链接已复制到剪贴板。",
306
+ "toast.deleted": "已删除“{title}”。",
307
+ "toast.removed": "已从作品里移除“{title}”,文件夹还在原处。",
308
+ "toast.undo": "撤销",
309
+ "toast.close": "关闭",
310
+ "empty.title": "还没有作品",
311
+ "empty.body": "Agent 用 baoyu-design 的技能做出来的东西都会出现在这里。点上面的入口开始做一个。",
312
+ "empty.noMatches": "没有符合条件的作品",
313
+ "load.error": "读取作品列表失败:{message}",
314
+ "load.retry": "重试",
315
+ "open.pluginOff": "{plugin} 插件没有开启,打不开这个作品。",
316
+ "open.failed": "打不开:{message}",
317
+ "open.noSession": "原来的对话已不在,编辑器在新窗口里打开了。",
318
+ "action.failed": "没有做成:{message}"
319
+ };
320
+ var en = {
321
+ panel: "Artifacts",
322
+ title: "Artifacts",
323
+ "time.locale": "en",
324
+ search: "Search artifacts",
325
+ "search.clear": "Clear search",
326
+ "type.claude-slides": "Slides",
327
+ "type.claude-design": "Design",
328
+ "type.claude-design-system": "Design system",
329
+ "type.claude-dashboard": "Dashboard",
330
+ "type.claude-docs": "Docs",
331
+ "type.claude-motion": "Motion",
332
+ "create.heading": "Make something new",
333
+ "create.new": "New",
334
+ "create.session": "New session",
335
+ "prompt.claude-slides": "Make me a slide deck about: ",
336
+ "prompt.claude-design": "Design this for me: ",
337
+ "prompt.claude-design-system": "Make me a design system for this brand: ",
338
+ "prompt.claude-dashboard": "Make me a dashboard from this data: ",
339
+ "prompt.claude-docs": "Write me a doc about: ",
340
+ "prompt.claude-motion": "Make me an animation about: ",
341
+ "view.list": "List view",
342
+ "view.grid": "Grid view",
343
+ "filter.button": "Sort and filter",
344
+ "filter.sort": "Sort by",
345
+ "filter.type": "Type",
346
+ "filter.date": "Date opened",
347
+ "filter.sharing": "Sharing",
348
+ "filter.pinned": "Pinned",
349
+ "filter.any": "Any",
350
+ "filter.some": "{count} selected",
351
+ "filter.clear": "Clear filters",
352
+ "sort.opened": "Last opened",
353
+ "sort.updated": "Last edited",
354
+ "sort.created": "Date created",
355
+ "sort.name": "Name",
356
+ "date.any": "Any time",
357
+ "date.today": "Today",
358
+ "date.yesterday": "Yesterday",
359
+ "date.week": "Past 7 days",
360
+ "date.month": "Past 30 days",
361
+ "sharing.any": "Any",
362
+ "sharing.shared": "Shared",
363
+ "sharing.private": "Not shared",
364
+ "card.opened": "Opened {time}",
365
+ "card.justNow": "Opened just now",
366
+ "card.opening": "Opening…",
367
+ "card.shared": "Shared on the local network",
368
+ "action.more": "More actions",
369
+ "action.pin": "Pin",
370
+ "action.unpin": "Unpin",
371
+ "action.rename": "Rename",
372
+ "action.copy": "Copy link",
373
+ "action.delete": "Delete",
374
+ "rename.title": "Rename artifact",
375
+ "rename.save": "Save",
376
+ "dialog.cancel": "Cancel",
377
+ "delete.title": "Delete {title}?",
378
+ "delete.moves": "This removes the artifact and moves its folder to baoyu-design/trash in the app's data folder.",
379
+ "delete.keeps": "This takes it off your artifacts. Its folder holds other files too, so it stays where it is: {path}",
380
+ "delete.confirm": "Delete",
381
+ "toast.copied": "Link copied to clipboard.",
382
+ "toast.deleted": "Deleted “{title}”.",
383
+ "toast.removed": "Took “{title}” off your artifacts. Its folder stays where it is.",
384
+ "toast.undo": "Undo",
385
+ "toast.close": "Close",
386
+ "empty.title": "No artifacts yet",
387
+ "empty.body": "What the agent makes with a baoyu-design skill shows up here. Start one with a tile above.",
388
+ "empty.noMatches": "No artifacts match",
389
+ "load.error": "Could not read the artifacts: {message}",
390
+ "load.retry": "Try again",
391
+ "open.pluginOff": "The {plugin} plugin is off, so this artifact cannot open.",
392
+ "open.failed": "Could not open it: {message}",
393
+ "open.noSession": "The conversation is gone, so the editor opened in a new window.",
394
+ "action.failed": "That did not work: {message}"
395
+ };
396
+ var SKILLS = ["claude-docs", "claude-slides", "claude-design", "claude-dashboard", "claude-design-system", "claude-motion"];
397
+ var isSkill = (s) => SKILLS.includes(s);
398
+ var creatable = (on) => SKILLS.filter((s) => on.includes(s));
399
+ var promptFor = (skill, t) => `/${skill} ${t(`prompt.${skill}`)}`;
400
+ var NO_FILTERS = { sort: "opened", types: [], date: "any", sharing: "any", pinned: false };
401
+ var SORTS = ["opened", "updated", "created", "name"];
402
+ var DATES = ["any", "today", "yesterday", "week", "month"];
403
+ var SHARINGS = ["any", "shared", "private"];
404
+ var isFiltered = (f) => f.types.length > 0 || f.date !== "any" || f.sharing !== "any" || f.pinned;
405
+ function inRange(iso, range, now) {
406
+ if (range === "any") return true;
407
+ const then = Date.parse(iso);
408
+ if (!Number.isFinite(then)) return false;
409
+ const day = new Date(now);
410
+ const today = new Date(day.getFullYear(), day.getMonth(), day.getDate()).getTime();
411
+ const DAY = 864e5;
412
+ switch (range) {
413
+ case "today":
414
+ return then >= today;
415
+ case "yesterday":
416
+ return then >= new Date(day.getFullYear(), day.getMonth(), day.getDate() - 1).getTime() && then < today;
417
+ case "week":
418
+ return then >= now - 7 * DAY;
419
+ case "month":
420
+ return then >= now - 30 * DAY;
421
+ }
422
+ }
423
+ function sorted(views, sort, locale) {
424
+ const by = (key) => (a, b) => a[key] < b[key] ? 1 : a[key] > b[key] ? -1 : 0;
425
+ const compare = sort === "name" ? (a, b) => a.title.localeCompare(b.title, locale) : by(sort === "updated" ? "updatedAt" : sort === "created" ? "firstOpenedAt" : "openedAt");
426
+ return [...views].sort(compare);
427
+ }
428
+ var CSS = `
429
+ .bda-page{position:relative;width:100%;min-width:0;height:100%;min-height:0;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-base);font-size:14px;line-height:1.6;display:flex;flex-direction:column;overflow:hidden}
430
+ .bda-scroll{flex:1;min-height:0;overflow:auto;scrollbar-gutter:stable}
431
+ .bda-content{max-width:1120px;margin:0 auto;padding:0 clamp(24px,4vw,48px) 56px}
432
+ .bda-heading{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:12px 16px;padding-top:28px;margin-bottom:24px}
433
+ [data-platform=darwin] .bda-heading{padding-top:calc(28px + var(--dsh-frame-top-clearance,0px))}
434
+ .bda-heading h1{margin:0;font-size:20px;font-weight:500;line-height:28px}
435
+ .bda-tools{display:flex;align-items:center;gap:4px;margin-left:auto}
436
+ .bda-tool{position:relative;display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;padding:0;border:0;border-radius:8px;background:none;color:var(--dsw-alias-label-secondary);cursor:pointer}
437
+ .bda-tool:hover,.bda-tool[aria-expanded=true]{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}
438
+ .bda-tool[data-active=true]::after{content:"";position:absolute;top:6px;right:6px;width:6px;height:6px;border-radius:50%;background:var(--dsw-alias-state-business-primary)}
439
+ .bda-search{display:flex;align-items:center;gap:6px;width:min(240px,50vw);height:32px;padding:0 8px 0 10px;border:.5px solid var(--dsw-alias-border-l2);border-radius:8px;color:var(--dsw-alias-label-tertiary)}
440
+ .bda-search:focus-within{border-color:var(--dsw-alias-state-business-primary)}
441
+ .bda-search input{flex:1;min-width:0;height:100%;border:0;padding:0;background:none;color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;outline:none}
442
+ .bda-search input::placeholder{color:var(--dsw-alias-label-caption)}
443
+ .bda-search input::-webkit-search-cancel-button{display:none}
444
+ .bda-search button{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;margin-right:-4px;padding:0;border:0;border-radius:6px;background:none;color:inherit;cursor:pointer}
445
+ .bda-search button:hover{background:var(--dsw-alias-interactive-bg-hover)}
446
+ .bda-section-title{margin:0 0 12px;font-size:13px;font-weight:400;line-height:20px;color:var(--dsw-alias-label-tertiary)}
447
+ .bda-tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(118px,1fr));gap:14px;margin:0 0 36px;padding:0;list-style:none}
448
+ .bda-tile{display:flex;flex-direction:column;gap:8px;width:100%;padding:0;border:0;background:none;color:inherit;font:inherit;text-align:left;cursor:pointer}
449
+ .bda-tile-art{position:relative;display:flex;align-items:center;justify-content:center;aspect-ratio:7/5;border-radius:14px;overflow:hidden;background:color-mix(in srgb,var(--bda-tint) 24%,var(--dsw-alias-bg-base));color:color-mix(in srgb,var(--bda-tint) 78%,var(--dsw-alias-label-primary));transition:background-color .15s}
450
+ .bda-tile:hover .bda-tile-art{background:color-mix(in srgb,var(--bda-tint) 34%,var(--dsw-alias-bg-base))}
451
+ .bda-tile:focus-visible{outline:none}
452
+ .bda-tile:focus-visible .bda-tile-art{outline:var(--dsw-focus-ring-width,2px) solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:2px}
453
+ .bda-tile-squiggle{position:absolute;inset:0;width:100%;height:100%;opacity:.28}
454
+ .bda-tile-label{padding-left:2px;font-size:14px;line-height:20px}
455
+ .bda-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:20px;margin:0;padding:0;list-style:none}
456
+ .bda-item{position:relative;min-width:0}
457
+ .bda-card{display:flex;flex-direction:column;width:100%;padding:0;border:.5px solid var(--dsw-alias-border-l2);border-radius:14px;overflow:hidden;background:var(--dsw-alias-bg-base);color:inherit;font:inherit;text-align:left;cursor:pointer;transition:background-color .15s}
458
+ .bda-item:hover .bda-card{background:var(--dsw-alias-interactive-bg-hover)}
459
+ .bda-card:disabled{cursor:progress}
460
+ .bda-card:focus-visible,.bda-row:focus-visible{outline:var(--dsw-focus-ring-width,2px) solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:2px}
461
+ .bda-preview{position:relative;display:block;width:100%;aspect-ratio:2/1;overflow:hidden;background:color-mix(in srgb,var(--bda-tint) 16%,var(--dsw-alias-bg-base))}
462
+ .bda-sheet{position:absolute;left:12%;right:12%;top:13%;bottom:-8px;border-radius:8px 8px 0 0;overflow:hidden;background:var(--dsw-alias-bg-base);box-shadow:0 0 0 .5px var(--dsw-alias-border-l2),0 6px 18px -8px rgba(0,0,0,.28);transition:transform .2s}
463
+ .bda-item:hover .bda-sheet{transform:translateY(-3px)}
464
+ .bda-sheet img{display:block;width:100%;height:100%;object-fit:cover;object-position:top center;user-select:none}
465
+ .bda-preview[data-stack=true] .bda-sheet{left:20%;right:20%}
466
+ .bda-preview[data-stack=true]::before{content:"";position:absolute;left:21%;right:19%;top:10%;bottom:-8px;border-radius:8px 8px 0 0;background:var(--dsw-alias-bg-base);box-shadow:0 0 0 .5px var(--dsw-alias-border-l2);transform:rotate(1.6deg);transform-origin:bottom center}
467
+ .bda-paper{position:absolute;left:14%;right:14%;top:13%;bottom:-8px;border-radius:8px 8px 0 0;background:var(--dsw-alias-bg-base);box-shadow:0 0 0 .5px var(--dsw-alias-border-l2),0 6px 18px -8px rgba(0,0,0,.25);padding:6% 7% 0;display:flex;flex-direction:column;gap:6px;overflow:hidden;transition:transform .2s}
468
+ .bda-item:hover .bda-paper{transform:translateY(-3px)}
469
+ .bda-paper-title{font-size:11px;line-height:1.35;font-weight:600;color:var(--dsw-alias-label-primary);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere}
470
+ .bda-line{height:5px;border-radius:3px;background:var(--dsw-alias-border-l2)}
471
+ .bda-slide{position:absolute;left:14%;right:14%;top:13%;aspect-ratio:16/9;border-radius:6px;background:var(--dsw-alias-bg-base);box-shadow:0 0 0 .5px var(--dsw-alias-border-l2),0 6px 18px -8px rgba(0,0,0,.25);display:flex;flex-direction:column;justify-content:center;gap:6px;padding:0 9%;transition:transform .2s}
472
+ .bda-item:hover .bda-slide{transform:translateY(-3px)}
473
+ .bda-slide .bda-paper-title{font-size:12px}
474
+ .bda-slide .bda-line{width:40%;background:var(--bda-tint);opacity:.55}
475
+ .bda-boards{display:flex;gap:6px;flex:1;margin-top:2px}
476
+ .bda-board{flex:1;border-radius:3px;background:color-mix(in srgb,var(--bda-tint) 18%,var(--dsw-alias-bg-base));box-shadow:0 0 0 .5px var(--dsw-alias-border-l2)}
477
+ .bda-board:nth-child(2){flex:.7;margin-top:10%}
478
+ .bda-swatches{display:flex;gap:4px}
479
+ .bda-swatch{width:16px;height:16px;border-radius:50%;background:var(--bda-tint)}
480
+ .bda-swatch:nth-child(2){opacity:.7}.bda-swatch:nth-child(3){opacity:.45}.bda-swatch:nth-child(4){opacity:.25}
481
+ .bda-specimen{font-size:20px;line-height:1;font-weight:600;color:var(--dsw-alias-label-secondary)}
482
+ .bda-kpis{display:flex;gap:5px}
483
+ .bda-kpi{flex:1;height:16px;border-radius:3px;box-shadow:0 0 0 .5px var(--dsw-alias-border-l2)}
484
+ .bda-bars{display:flex;align-items:flex-end;gap:5px;height:34%;margin-top:2px}
485
+ .bda-bar{flex:1;border-radius:2px 2px 0 0;background:var(--bda-tint);opacity:.7}
486
+ .bda-film{position:absolute;left:14%;right:14%;top:13%;aspect-ratio:16/9;border-radius:6px;background:#1d1d1f;box-shadow:0 6px 18px -8px rgba(0,0,0,.35);display:flex;align-items:center;justify-content:center;transition:transform .2s;color:#fff}
487
+ .bda-item:hover .bda-film{transform:translateY(-3px)}
488
+ .bda-film-bar{position:absolute;left:8%;right:8%;bottom:10%;height:3px;border-radius:2px;background:rgba(255,255,255,.25)}
489
+ .bda-film-bar::after{content:"";position:absolute;left:0;top:0;bottom:0;width:38%;border-radius:2px;background:var(--bda-tint)}
490
+ .bda-film-title{position:absolute;left:8%;right:8%;top:10%;font-size:10px;font-weight:600;color:rgba(255,255,255,.85);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
491
+ .bda-busy{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;gap:8px;background:color-mix(in srgb,var(--dsw-alias-bg-base) 70%,transparent);color:var(--dsw-alias-label-secondary);font-size:13px}
492
+ .bda-spinner{width:14px;height:14px;border-radius:50%;border:1.5px solid currentColor;border-right-color:transparent;animation:bda-spin .8s linear infinite}
493
+ @keyframes bda-spin{to{transform:rotate(360deg)}}
494
+ .bda-info{display:flex;align-items:center;gap:12px;min-width:0;padding:12px 14px}
495
+ .bda-glyph{flex:none;display:flex;align-items:center;justify-content:center;width:36px;height:36px;border-radius:9px;background:color-mix(in srgb,var(--bda-tint) 16%,var(--dsw-alias-bg-base));color:color-mix(in srgb,var(--bda-tint) 78%,var(--dsw-alias-label-primary))}
496
+ .bda-text{display:flex;flex-direction:column;min-width:0;flex:1}
497
+ .bda-name{font-weight:500;line-height:20px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
498
+ .bda-meta{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
499
+ .bda-actions{position:absolute;top:10px;right:10px;display:flex;align-items:center;gap:6px}
500
+ .bda-act{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;padding:0;border:.5px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-secondary);cursor:pointer;opacity:0;transition:opacity .12s}
501
+ .bda-act:hover{color:var(--dsw-alias-label-primary);background:color-mix(in srgb,var(--dsw-alias-label-primary) 6%,var(--dsw-alias-bg-base))}
502
+ .bda-item:hover .bda-act,.bda-item:focus-within .bda-act,.bda-act[aria-expanded=true],.bda-act[data-on=true]{opacity:1}
503
+ .bda-act[data-on=true]{color:var(--dsw-alias-label-primary)}
504
+ .bda-list{display:flex;flex-direction:column;gap:2px;margin:0;padding:0;list-style:none}
505
+ .bda-list .bda-item{display:flex;align-items:center;border-radius:10px;padding-right:8px}
506
+ .bda-list .bda-item:hover,.bda-list .bda-item[data-menu=true]{background:var(--dsw-alias-interactive-bg-hover)}
507
+ .bda-row{display:flex;align-items:center;gap:12px;flex:1;min-width:0;padding:8px 10px;border:0;border-radius:10px;background:none;color:inherit;font:inherit;text-align:left;cursor:pointer}
508
+ .bda-row:disabled{cursor:progress}
509
+ .bda-row .bda-name{flex:1;min-width:0}
510
+ .bda-row .bda-meta{flex:none;margin-left:16px}
511
+ .bda-row-tools{flex:none;display:flex;align-items:center;gap:2px;margin-left:4px;color:var(--dsw-alias-label-tertiary)}
512
+ .bda-shared{display:inline-flex;align-items:center;margin-right:6px;color:var(--dsw-alias-label-tertiary)}
513
+ .bda-list .bda-act{width:28px;height:28px;border:0;background:none}
514
+ .bda-list .bda-act:hover{background:color-mix(in srgb,var(--dsw-alias-label-primary) 8%,transparent)}
515
+ .bda-list .bda-act-more{opacity:1}
516
+ @media (max-width:560px){.bda-row .bda-meta{display:none}}
517
+ .bda-menu{position:fixed;z-index:1000;min-width:200px;padding:6px;border:.5px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-base);box-shadow:0 8px 28px -6px rgba(0,0,0,.28);font-size:14px}
518
+ .bda-menu-item{display:flex;align-items:center;gap:10px;width:100%;min-height:34px;padding:5px 10px;border:0;border-radius:8px;background:none;color:var(--dsw-alias-label-primary);font:inherit;text-align:left;white-space:nowrap;cursor:pointer}
519
+ .bda-menu-item:hover,.bda-menu-item:focus-visible,.bda-menu-item[aria-expanded=true]{background:var(--dsw-alias-interactive-bg-hover);outline:none}
520
+ .bda-menu-item[data-danger=true]{color:var(--dsw-alias-state-error-primary)}
521
+ .bda-menu-item svg{flex:none}
522
+ .bda-menu-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis}
523
+ .bda-menu-value{margin-left:16px;color:var(--dsw-alias-label-tertiary)}
524
+ .bda-menu-count{margin-left:16px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums}
525
+ .bda-menu-key{margin-left:20px;color:var(--dsw-alias-label-tertiary);font:inherit;font-size:12px}
526
+ .bda-menu-rule{height:.5px;margin:6px 4px;background:var(--dsw-alias-border-l2)}
527
+ .bda-menu-chev{margin-left:6px;color:var(--dsw-alias-label-tertiary)}
528
+ .bda-menu-row{position:relative}
529
+ .bda-submenu{position:absolute;left:calc(100% + 12px);top:-6px;z-index:1001;min-width:190px;padding:6px;border:.5px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-base);box-shadow:0 8px 28px -6px rgba(0,0,0,.28)}
530
+ .bda-submenu[data-flip=true]{left:auto;right:calc(100% + 12px)}
531
+ .bda-check{flex:none;display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border:1px solid var(--dsw-alias-border-l1);border-radius:5px;color:transparent}
532
+ .bda-check[data-on=true]{border-color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-state-business-primary);color:#fff}
533
+ .bda-tick{flex:none;margin-left:16px;color:var(--dsw-alias-state-business-primary)}
534
+ .bda-scrim{position:fixed;inset:0;z-index:1100;display:flex;align-items:center;justify-content:center;padding:24px;background:rgba(0,0,0,.36)}
535
+ .bda-dialog{width:min(420px,100%);padding:20px;border-radius:16px;background:var(--dsw-alias-bg-base);box-shadow:0 18px 48px -12px rgba(0,0,0,.4)}
536
+ .bda-dialog h2{margin:0 0 12px;font-size:16px;font-weight:600;line-height:24px;overflow-wrap:anywhere}
537
+ .bda-dialog p{margin:0;color:var(--dsw-alias-label-secondary);font-size:14px;line-height:1.55;overflow-wrap:anywhere}
538
+ .bda-dialog input{box-sizing:border-box;width:100%;height:38px;padding:0 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:none;color:var(--dsw-alias-label-primary);font:inherit;outline:none}
539
+ .bda-dialog input:focus{border-color:var(--dsw-alias-state-business-primary)}
540
+ .bda-dialog-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:20px}
541
+ .bda-btn{height:34px;padding:0 14px;border:0;border-radius:9px;background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);font:inherit;font-weight:500;cursor:pointer}
542
+ .bda-btn:hover{background:color-mix(in srgb,var(--dsw-alias-label-primary) 12%,var(--dsw-alias-bg-base))}
543
+ .bda-btn:disabled{color:var(--dsw-alias-label-caption);cursor:default;background:var(--dsw-alias-interactive-bg-hover)}
544
+ .bda-btn[data-primary=true]{background:var(--dsw-alias-state-business-primary);color:#fff}
545
+ .bda-btn[data-primary=true]:hover:not(:disabled){filter:brightness(1.08)}
546
+ .bda-btn[data-primary=true]:disabled{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-caption)}
547
+ .bda-btn[data-danger=true]{border:1px solid var(--dsw-alias-state-error-primary);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 10%,var(--dsw-alias-bg-base));color:var(--dsw-alias-state-error-primary)}
548
+ .bda-btn[data-danger=true]:hover{background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 18%,var(--dsw-alias-bg-base))}
549
+ .bda-toast{position:absolute;left:50%;bottom:24px;z-index:900;display:flex;align-items:center;gap:10px;max-width:min(560px,calc(100% - 32px));padding:9px 10px 9px 14px;border:.5px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-base);box-shadow:0 8px 28px -6px rgba(0,0,0,.28);transform:translateX(-50%);font-size:14px}
550
+ .bda-toast[data-kind=error]{color:var(--dsw-alias-state-error-primary)}
551
+ .bda-toast svg{flex:none}
552
+ .bda-toast-text{min-width:0;overflow-wrap:anywhere}
553
+ .bda-toast-undo{flex:none;padding:2px 8px;border:0;border-radius:6px;background:none;color:var(--dsw-alias-state-business-primary);font:inherit;font-weight:500;cursor:pointer}
554
+ .bda-toast-undo:hover{background:var(--dsw-alias-interactive-bg-hover)}
555
+ .bda-toast-close{flex:none;display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;border:0;border-radius:6px;background:none;color:var(--dsw-alias-label-tertiary);cursor:pointer}
556
+ .bda-toast-close:hover{background:var(--dsw-alias-interactive-bg-hover)}
557
+ .bda-empty{display:flex;flex-direction:column;align-items:center;gap:8px;padding:56px 20px;text-align:center;color:var(--dsw-alias-label-tertiary)}
558
+ .bda-empty svg{color:var(--dsw-alias-label-caption);margin-bottom:4px}
559
+ .bda-empty h2{margin:0;font-size:14px;font-weight:500;color:var(--dsw-alias-label-secondary)}
560
+ .bda-empty p{margin:0;max-width:420px;font-size:13px;line-height:1.6}
561
+ .bda-retry{margin-top:8px;height:30px;padding:0 12px;border:.5px solid var(--dsw-alias-border-l2);border-radius:15px;background:none;color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;cursor:pointer}
562
+ .bda-retry:hover{background:var(--dsw-alias-interactive-bg-hover)}
563
+ .bda-skeleton .bda-card{cursor:default}
564
+ .bda-skeleton .bda-preview{background:var(--dsw-alias-bg-skeleton,var(--dsw-alias-interactive-bg-hover))}
565
+ .bda-skeleton .bda-line{width:60%;height:10px;margin:18px 14px}
566
+ @media (prefers-reduced-motion:reduce){.bda-sheet,.bda-paper,.bda-slide,.bda-film,.bda-act,.bda-tile-art,.bda-card{transition:none}.bda-spinner{animation-duration:2s}}
567
+ `;
568
+ var TINTS = {
569
+ "claude-slides": "#e8703a",
570
+ "claude-design": "#7a5cf0",
571
+ "claude-design-system": "#c79a1b",
572
+ "claude-dashboard": "#e5484d",
573
+ "claude-docs": "#3f8ef0",
574
+ "claude-motion": "#1fa971"
575
+ };
576
+ var tintOf = (skill) => isSkill(skill) ? TINTS[skill] : "#8a8a8a";
577
+ var svg = (size) => ({ display: "block", width: size, height: size, flex: "none" });
578
+ function ArtifactsIcon({ size = 16 }) {
579
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 20 20", fill: "none", "aria-hidden": "true", style: svg(size), children: [
580
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "3", y: "6", width: "8.5", height: "11", rx: "2", stroke: "currentColor", strokeWidth: "1.5" }),
581
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8.5 6V5a2 2 0 0 1 2-2h4.5a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2h-3.5", stroke: "currentColor", strokeWidth: "1.5" })
582
+ ] });
583
+ }
584
+ function TypeIcon({ skill, size = 18 }) {
585
+ const p = { stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round" };
586
+ const body = (() => {
587
+ switch (skill) {
588
+ case "claude-slides":
589
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M2.75 3.5h14.5M4 3.5v8.25a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3.5M7.25 16.75 10 12.75l2.75 4", ...p }) });
590
+ case "claude-design":
591
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
592
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M10 2.75a7.25 7.25 0 1 0 0 14.5c1.1 0 1.75-.8 1.75-1.7 0-.5-.2-.9-.45-1.25-.25-.35-.45-.7-.45-1.15 0-.95.75-1.65 1.7-1.65h1.6a3.1 3.1 0 0 0 3.1-3.1c0-3.1-3.2-5.65-7.25-5.65Z", ...p }),
593
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "6.4", cy: "9.4", r: ".9", fill: "currentColor" }),
594
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "8.8", cy: "6.3", r: ".9", fill: "currentColor" }),
595
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12.5", cy: "6.8", r: ".9", fill: "currentColor" })
596
+ ] });
597
+ case "claude-design-system":
598
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
599
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "6.5", cy: "6.5", r: "3.25", ...p }),
600
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "13.5", cy: "6.5", r: "3.25", ...p }),
601
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "10", cy: "13", r: "3.25", ...p })
602
+ ] });
603
+ case "claude-dashboard":
604
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
605
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "2.75", y: "10", width: "4.5", height: "7.25", rx: "1", ...p }),
606
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "7.75", y: "2.75", width: "4.5", height: "14.5", rx: "1", ...p }),
607
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "12.75", y: "7", width: "4.5", height: "10.25", rx: "1", ...p })
608
+ ] });
609
+ case "claude-docs":
610
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
611
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5.5 2.75h6l4 4v9.5a1 1 0 0 1-1 1h-9a1 1 0 0 1-1-1V3.75a1 1 0 0 1 1-1Z", ...p }),
612
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M11.5 2.75v4h4", ...p })
613
+ ] });
614
+ case "claude-motion":
615
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
616
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "2.75", y: "4", width: "14.5", height: "12", rx: "2", ...p }),
617
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m8.5 7.75 4 2.25-4 2.25v-4.5Z", ...p })
618
+ ] });
619
+ default:
620
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "3", y: "3", width: "14", height: "14", rx: "2", ...p });
621
+ }
622
+ })();
623
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 20 20", fill: "none", "aria-hidden": "true", style: svg(size), children: body });
624
+ }
625
+ function Icon({ d, size = 16, fill }) {
626
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 16 16", fill: fill ? "currentColor" : "none", stroke: fill ? void 0 : "currentColor", strokeWidth: "1.3", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", style: svg(size), children: typeof d === "string" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d }) : d });
627
+ }
628
+ var ICON = {
629
+ search: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
630
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "7", cy: "7", r: "4.5" }),
631
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m10.5 10.5 3 3" })
632
+ ] }),
633
+ clear: "m4.5 4.5 7 7m0-7-7 7",
634
+ filter: "M2.5 4.5h11M4.5 8h7M6.5 11.5h3",
635
+ list: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
636
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5.75 4h7.75M5.75 8h7.75M5.75 12h7.75" }),
637
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M2.5 4h.01M2.5 8h.01M2.5 12h.01", strokeWidth: "1.8" })
638
+ ] }),
639
+ grid: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
640
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "2.5", y: "2.5", width: "4.5", height: "4.5", rx: "1" }),
641
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "9", y: "2.5", width: "4.5", height: "4.5", rx: "1" }),
642
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "2.5", y: "9", width: "4.5", height: "4.5", rx: "1" }),
643
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "9", y: "9", width: "4.5", height: "4.5", rx: "1" })
644
+ ] }),
645
+ plus: "M8 3v10M3 8h10",
646
+ chat: "M3 3.5h10a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H8.2L5.4 13.8a.3.3 0 0 1-.5-.23V11.5H3a1 1 0 0 1-1-1v-6a1 1 0 0 1 1-1Z",
647
+ pin: "M9.75 2.25 13.75 6.25M10.75 3.25 7 6.25 4.5 6.5 9.5 11.5 9.75 9 12.75 5.25M7 9l-4 4",
648
+ more: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
649
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "3.5", cy: "8", r: ".6", fill: "currentColor" }),
650
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "8", cy: "8", r: ".6", fill: "currentColor" }),
651
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12.5", cy: "8", r: ".6", fill: "currentColor" })
652
+ ] }),
653
+ rename: "M10.5 3 13 5.5 5.5 13 2.5 13.5 3 10.5 10.5 3Z",
654
+ link: "M6.75 9.25a2.5 2.5 0 0 0 3.54 0l2.25-2.25a2.5 2.5 0 0 0-3.54-3.54L8.25 4.2M9.25 6.75a2.5 2.5 0 0 0-3.54 0L3.46 9a2.5 2.5 0 0 0 3.54 3.54l.75-.75",
655
+ trash: "M2.75 4.25h10.5M6.25 4.25V3a.75.75 0 0 1 .75-.75h2a.75.75 0 0 1 .75.75v1.25M4 4.25l.6 8.6a1 1 0 0 0 1 .9h4.8a1 1 0 0 0 1-.9l.6-8.6M6.75 7v4M9.25 7v4",
656
+ info: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
657
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "8", cy: "8", r: "6" }),
658
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 7.5v3.25M8 5.25h.01" })
659
+ ] }),
660
+ chevron: "m6 3.5 4.5 4.5L6 12.5",
661
+ tick: "m3 8.5 3.25 3.25L13 4.75",
662
+ people: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
663
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "6", cy: "5.5", r: "2.25" }),
664
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M2 13c0-2.2 1.8-3.75 4-3.75S10 10.8 10 13M10.5 3.5a2.25 2.25 0 0 1 0 4M12 9.5c1.3.45 2.25 1.75 2.25 3.5" })
665
+ ] })
666
+ };
667
+ function Drawn({ skill, title }) {
668
+ const name = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "bda-paper-title", children: title });
669
+ switch (skill) {
670
+ case "claude-slides":
671
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bda-slide", children: [
672
+ name,
673
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "bda-line" })
674
+ ] });
675
+ case "claude-motion":
676
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bda-film", children: [
677
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "bda-film-title", children: title }),
678
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 20 20", "aria-hidden": "true", style: svg(22), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M7 5.5v9l7.5-4.5L7 5.5Z", fill: "currentColor" }) }),
679
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "bda-film-bar" })
680
+ ] });
681
+ case "claude-design":
682
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bda-paper", children: [
683
+ name,
684
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bda-boards", children: [
685
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "bda-board" }),
686
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "bda-board" })
687
+ ] })
688
+ ] });
689
+ case "claude-design-system":
690
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bda-paper", children: [
691
+ name,
692
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bda-swatches", children: [
693
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-swatch" }),
694
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-swatch" }),
695
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-swatch" }),
696
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-swatch" })
697
+ ] }),
698
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "bda-specimen", children: "Aa" })
699
+ ] });
700
+ case "claude-dashboard":
701
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bda-paper", children: [
702
+ name,
703
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bda-kpis", children: [
704
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-kpi" }),
705
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-kpi" }),
706
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-kpi" })
707
+ ] }),
708
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "bda-bars", children: [45, 70, 55, 90, 65, 80].map((h, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-bar", style: { height: `${h}%` } }, i)) })
709
+ ] });
710
+ default:
711
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bda-paper", children: [
712
+ name,
713
+ [92, 100, 84, 96, 70].map((w, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "bda-line", style: { width: `${w}%` } }, i))
714
+ ] });
715
+ }
716
+ }
717
+ function Preview({ view, thumbnailUrl }) {
718
+ const url = thumbnailUrl(view);
719
+ const [broken, setBroken] = (0, import_react.useState)(null);
720
+ const shown = url !== null && broken !== url;
721
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-preview", "data-stack": shown && view.skill === "claude-docs", children: shown ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-sheet", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: url, alt: "", draggable: false, onError: () => setBroken(url) }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Drawn, { skill: view.skill, title: view.title }) });
722
+ }
723
+ function ago(iso, now, t) {
724
+ const then = Date.parse(iso);
725
+ if (!Number.isFinite(then)) return "";
726
+ const seconds = Math.round((then - now) / 1e3);
727
+ if (seconds > -60) return t("card.justNow");
728
+ const steps = [["minute", 60], ["hour", 3600], ["day", 86400], ["week", 604800], ["month", 2629800], ["year", 31557600]];
729
+ let unit = "minute";
730
+ let size = 60;
731
+ for (const [u, s] of steps) if (-seconds >= s) [unit, size] = [u, s];
732
+ let format;
733
+ try {
734
+ format = new Intl.RelativeTimeFormat(t("time.locale"), { numeric: "auto" });
735
+ } catch {
736
+ format = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
737
+ }
738
+ return t("card.opened", { time: format.format(Math.round(seconds / size), unit) });
739
+ }
740
+ function useNow() {
741
+ const [now, setNow] = (0, import_react.useState)(() => Date.now());
742
+ (0, import_react.useEffect)(() => {
743
+ const timer = setInterval(() => setNow(Date.now()), 6e4);
744
+ return () => clearInterval(timer);
745
+ }, []);
746
+ return now;
747
+ }
748
+ var ITEMS = "[role^=menuitem]";
749
+ function menuKeys(event, close) {
750
+ if (event.key === "Escape") {
751
+ event.preventDefault();
752
+ event.stopPropagation();
753
+ close();
754
+ return;
755
+ }
756
+ if (event.key !== "ArrowDown" && event.key !== "ArrowUp") return;
757
+ event.preventDefault();
758
+ event.stopPropagation();
759
+ const items = [...event.currentTarget.querySelectorAll(ITEMS)].filter((item) => item.closest("[role=menu]") === event.currentTarget);
760
+ const at = items.indexOf(document.activeElement);
761
+ items[(at + (event.key === "ArrowDown" ? 1 : items.length - 1)) % items.length]?.focus();
762
+ }
763
+ function Menu({ anchor, label, onClose, onKey, children }) {
764
+ const ref = (0, import_react.useRef)(null);
765
+ const [place, setPlace] = (0, import_react.useState)({ visibility: "hidden" });
766
+ (0, import_react.useLayoutEffect)(() => {
767
+ const box = anchor.getBoundingClientRect();
768
+ const height = ref.current?.offsetHeight ?? 0;
769
+ const width = ref.current?.offsetWidth ?? 0;
770
+ const right = Math.max(8, window.innerWidth - box.right);
771
+ const fits = box.bottom + 6 + height <= window.innerHeight - 8;
772
+ setPlace({
773
+ right: window.innerWidth - right - width < 8 ? window.innerWidth - width - 8 : right,
774
+ ...fits ? { top: box.bottom + 6 } : { bottom: Math.max(8, window.innerHeight - box.top + 6) }
775
+ });
776
+ }, [anchor]);
777
+ const placed = place.visibility !== "hidden";
778
+ (0, import_react.useEffect)(() => {
779
+ if (placed) ref.current?.querySelector(ITEMS)?.focus({ preventScroll: true });
780
+ }, [placed]);
781
+ (0, import_react.useEffect)(() => {
782
+ const away = (event) => {
783
+ const target = event.target;
784
+ if (!ref.current?.contains(target) && !anchor.contains(target)) onClose();
785
+ };
786
+ const gone = () => onClose();
787
+ document.addEventListener("pointerdown", away, true);
788
+ window.addEventListener("resize", gone);
789
+ return () => {
790
+ document.removeEventListener("pointerdown", away, true);
791
+ window.removeEventListener("resize", gone);
792
+ };
793
+ }, [anchor, onClose]);
794
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
795
+ "div",
796
+ {
797
+ ref,
798
+ className: "bda-menu",
799
+ role: "menu",
800
+ "aria-label": label,
801
+ style: place,
802
+ onKeyDown: (event) => {
803
+ if (onKey && !event.metaKey && !event.ctrlKey && !event.altKey && onKey(event.key.toLowerCase())) {
804
+ event.preventDefault();
805
+ return;
806
+ }
807
+ menuKeys(event, () => {
808
+ onClose();
809
+ anchor.focus();
810
+ });
811
+ },
812
+ children
813
+ }
814
+ );
815
+ }
816
+ function ChoiceItem({ label, on, count, onPick }) {
817
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "bda-menu-item", role: "menuitemradio", "aria-checked": on, onClick: onPick, children: [
818
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-menu-label", children: label }),
819
+ count !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-menu-count", children: count }),
820
+ on && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-tick", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { d: ICON.tick }) })
821
+ ] });
822
+ }
823
+ function SubMenu({ id, label, value, open, setOpen, children }) {
824
+ const isOpen = open === id;
825
+ const panel = (0, import_react.useRef)(null);
826
+ const [flip, setFlip] = (0, import_react.useState)(false);
827
+ (0, import_react.useLayoutEffect)(() => {
828
+ if (isOpen && panel.current) setFlip(panel.current.getBoundingClientRect().right + (flip ? panel.current.offsetWidth : 0) > window.innerWidth - 8);
829
+ }, [isOpen]);
830
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bda-menu-row", onMouseEnter: () => setOpen(id), children: [
831
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
832
+ "button",
833
+ {
834
+ type: "button",
835
+ className: "bda-menu-item",
836
+ role: "menuitem",
837
+ "aria-haspopup": "menu",
838
+ "aria-expanded": isOpen,
839
+ onClick: () => setOpen(isOpen ? null : id),
840
+ onKeyDown: (event) => {
841
+ if (event.key === "ArrowRight" || event.key === "Enter" || event.key === " ") {
842
+ event.preventDefault();
843
+ setOpen(id);
844
+ requestAnimationFrame(() => panel.current?.querySelector(ITEMS)?.focus());
845
+ }
846
+ },
847
+ children: [
848
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-menu-label", children: label }),
849
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-menu-value", children: value }),
850
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-menu-chev", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { d: ICON.chevron, size: 12 }) })
851
+ ]
852
+ }
853
+ ),
854
+ isOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
855
+ "div",
856
+ {
857
+ ref: panel,
858
+ className: "bda-submenu",
859
+ role: "menu",
860
+ "aria-label": label,
861
+ "data-flip": flip,
862
+ onKeyDown: (event) => {
863
+ if (event.key === "ArrowLeft" || event.key === "Escape") {
864
+ event.preventDefault();
865
+ event.stopPropagation();
866
+ setOpen(null);
867
+ event.currentTarget.previousElementSibling?.focus();
868
+ return;
869
+ }
870
+ menuKeys(event, () => setOpen(null));
871
+ },
872
+ children
873
+ }
874
+ )
875
+ ] });
876
+ }
877
+ function FilterMenu({ anchor, filters, setFilters, types, counts, t, onClose }) {
878
+ const [open, setOpen] = (0, import_react.useState)(null);
879
+ const typeLabel = filters.types.length === 0 ? t("filter.any") : filters.types.length === 1 ? t(`type.${filters.types[0]}`) : t("filter.some", { count: String(filters.types.length) });
880
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Menu, { anchor, label: t("filter.button"), onClose, children: [
881
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SubMenu, { id: "sort", label: t("filter.sort"), value: t(`sort.${filters.sort}`), open, setOpen, children: SORTS.map((sort) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChoiceItem, { label: t(`sort.${sort}`), on: filters.sort === sort, onPick: () => setFilters({ ...filters, sort }) }, sort)) }),
882
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "bda-menu-rule", role: "separator" }),
883
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SubMenu, { id: "type", label: t("filter.type"), value: typeLabel, open, setOpen, children: types.map((type) => {
884
+ const on = filters.types.includes(type);
885
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
886
+ "button",
887
+ {
888
+ type: "button",
889
+ className: "bda-menu-item",
890
+ role: "menuitemcheckbox",
891
+ "aria-checked": on,
892
+ onClick: () => setFilters({ ...filters, types: on ? filters.types.filter((x) => x !== type) : [...filters.types, type] }),
893
+ children: [
894
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-check", "data-on": on, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { d: ICON.tick, size: 12 }) }),
895
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-menu-label", children: t(`type.${type}`) }),
896
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-menu-count", children: counts.get(type) ?? 0 })
897
+ ]
898
+ },
899
+ type
900
+ );
901
+ }) }),
902
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SubMenu, { id: "date", label: t("filter.date"), value: t(`date.${filters.date}`), open, setOpen, children: DATES.map((date) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChoiceItem, { label: t(`date.${date}`), on: filters.date === date, onPick: () => setFilters({ ...filters, date }) }, date)) }),
903
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SubMenu, { id: "sharing", label: t("filter.sharing"), value: t(`sharing.${filters.sharing}`), open, setOpen, children: SHARINGS.map((sharing) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChoiceItem, { label: t(`sharing.${sharing}`), on: filters.sharing === sharing, onPick: () => setFilters({ ...filters, sharing }) }, sharing)) }),
904
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "bda-menu-rule", role: "separator" }),
905
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { onMouseEnter: () => setOpen(null), children: [
906
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "bda-menu-item", role: "menuitemcheckbox", "aria-checked": filters.pinned, onClick: () => setFilters({ ...filters, pinned: !filters.pinned }), children: [
907
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-menu-label", children: t("filter.pinned") }),
908
+ filters.pinned && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-tick", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { d: ICON.tick }) })
909
+ ] }),
910
+ isFiltered(filters) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "bda-menu-item", role: "menuitem", onClick: () => setFilters({ ...NO_FILTERS, sort: filters.sort }), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-menu-label", children: t("filter.clear") }) })
911
+ ] })
912
+ ] });
913
+ }
914
+ function ActionsMenu({ anchor, view, t, onClose, act }) {
915
+ const item = (what, icon, label, key) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "bda-menu-item", role: "menuitem", "aria-keyshortcuts": key, onClick: () => act(what), children: [
916
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { d: icon }),
917
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-menu-label", children: label }),
918
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("kbd", { className: "bda-menu-key", children: key })
919
+ ] });
920
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
921
+ Menu,
922
+ {
923
+ anchor,
924
+ label: t("action.more"),
925
+ onClose,
926
+ onKey: (key) => {
927
+ const what = { p: "pin", r: "rename", c: "copy" }[key];
928
+ if (what === void 0) return false;
929
+ act(what);
930
+ return true;
931
+ },
932
+ children: [
933
+ item("pin", ICON.pin, t(view.pinned ? "action.unpin" : "action.pin"), "P"),
934
+ item("rename", ICON.rename, t("action.rename"), "R"),
935
+ item("copy", ICON.link, t("action.copy"), "C"),
936
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "bda-menu-rule", role: "separator" }),
937
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "bda-menu-item", role: "menuitem", "data-danger": "true", onClick: () => act("delete"), children: [
938
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { d: ICON.trash }),
939
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-menu-label", children: t("action.delete") })
940
+ ] })
941
+ ]
942
+ }
943
+ );
944
+ }
945
+ function CreateMenu({ anchor, skills, canCreate, t, onClose, create }) {
946
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Menu, { anchor, label: t("create.new"), onClose, children: [
947
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "bda-menu-item", role: "menuitem", disabled: !canCreate, onClick: () => create(), children: [
948
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { d: ICON.chat }),
949
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-menu-label", children: t("create.session") })
950
+ ] }),
951
+ skills.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "bda-menu-rule", role: "separator" }),
952
+ skills.map((skill) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "bda-menu-item", role: "menuitem", "data-skill": skill, onClick: () => create(skill), style: { "--bda-tint": tintOf(skill) }, children: [
953
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "color-mix(in srgb,var(--bda-tint) 78%,var(--dsw-alias-label-primary))" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TypeIcon, { skill, size: 16 }) }),
954
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-menu-label", children: t(`type.${skill}`) })
955
+ ] }, skill))
956
+ ] });
957
+ }
958
+ function Dialog({ label, onClose, children }) {
959
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
960
+ "div",
961
+ {
962
+ className: "bda-scrim",
963
+ onPointerDown: (event) => {
964
+ if (event.target === event.currentTarget) onClose();
965
+ },
966
+ onKeyDown: (event) => {
967
+ if (event.key === "Escape") {
968
+ event.stopPropagation();
969
+ onClose();
970
+ }
971
+ },
972
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "bda-dialog", role: "dialog", "aria-modal": "true", "aria-label": label, children })
973
+ }
974
+ );
975
+ }
976
+ function RenameDialog({ view, t, onClose, onSave }) {
977
+ const [title, setTitle] = (0, import_react.useState)(view.title);
978
+ const input = (0, import_react.useRef)(null);
979
+ (0, import_react.useEffect)(() => {
980
+ input.current?.focus();
981
+ input.current?.select();
982
+ }, []);
983
+ const clean = title.trim();
984
+ const ready = clean !== "" && clean !== view.title;
985
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Dialog, { label: t("rename.title"), onClose, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
986
+ "form",
987
+ {
988
+ onSubmit: (event) => {
989
+ event.preventDefault();
990
+ if (ready) onSave(clean);
991
+ },
992
+ children: [
993
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { children: t("rename.title") }),
994
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { ref: input, value: title, "aria-label": t("rename.title"), maxLength: 200, onChange: (event) => setTitle(event.target.value) }),
995
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bda-dialog-actions", children: [
996
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "bda-btn", onClick: onClose, children: t("dialog.cancel") }),
997
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "submit", className: "bda-btn", "data-primary": "true", disabled: !ready, children: t("rename.save") })
998
+ ] })
999
+ ]
1000
+ }
1001
+ ) });
1002
+ }
1003
+ function DeleteDialog({ view, t, onClose, onDelete }) {
1004
+ const confirm = (0, import_react.useRef)(null);
1005
+ (0, import_react.useEffect)(() => confirm.current?.focus(), []);
1006
+ const title = t("delete.title", { title: view.title });
1007
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Dialog, { label: title, onClose, children: [
1008
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { children: title }),
1009
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: view.deletable ? t("delete.moves") : t("delete.keeps", { path: view.root }) }),
1010
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bda-dialog-actions", children: [
1011
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "bda-btn", onClick: onClose, children: t("dialog.cancel") }),
1012
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { ref: confirm, type: "button", className: "bda-btn", "data-danger": "true", onClick: onDelete, children: t("delete.confirm") })
1013
+ ] })
1014
+ ] });
1015
+ }
1016
+ function ToastBar({ toast, t, onClose, onUndo }) {
1017
+ (0, import_react.useEffect)(() => {
1018
+ const timer = setTimeout(onClose, toast.undo || toast.kind === "error" ? 1e4 : 4e3);
1019
+ return () => clearTimeout(timer);
1020
+ }, [toast, onClose]);
1021
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bda-toast", role: toast.kind === "error" ? "alert" : "status", "data-kind": toast.kind, children: [
1022
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { d: ICON.info }),
1023
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-toast-text", children: toast.message }),
1024
+ toast.undo && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "bda-toast-undo", onClick: () => onUndo(toast.undo), children: t("toast.undo") }),
1025
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "bda-toast-close", "aria-label": t("toast.close"), onClick: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { d: ICON.clear, size: 14 }) })
1026
+ ] });
1027
+ }
1028
+ var hint = (view, conversation) => conversation ? `${view.root}
1029
+ ${conversation}` : view.root;
1030
+ function PinButton({ view, t, onPin }) {
1031
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "bda-act", "data-on": view.pinned, "aria-pressed": view.pinned, "aria-label": t(view.pinned ? "action.unpin" : "action.pin"), title: t(view.pinned ? "action.unpin" : "action.pin"), onClick: () => onPin(view), children: view.pinned ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 16 16", "aria-hidden": "true", style: svg(16), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M9.75 2.25 13.75 6.25M10.75 3.25 7 6.25 4.5 6.5 9.5 11.5 9.75 9 12.75 5.25M7 9l-4 4", fill: "currentColor", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round", strokeLinejoin: "round" }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { d: ICON.pin }) });
1032
+ }
1033
+ function MoreButton({ view, t, menuOpen, onMenu, extra }) {
1034
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: `bda-act ${extra ?? ""}`, "aria-label": t("action.more"), title: t("action.more"), "aria-haspopup": "menu", "aria-expanded": menuOpen, onClick: (event) => onMenu(view, event.currentTarget), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { d: ICON.more }) });
1035
+ }
1036
+ function Card({ view, state, conversation, now, t, thumbnailUrl, onOpen, onPin, menuOpen, onMenu }) {
1037
+ const opening = state.opening === view.root;
1038
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: "bda-item", "data-menu": menuOpen, style: { "--bda-tint": tintOf(view.skill) }, children: [
1039
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "bda-card", disabled: state.opening !== null, "aria-busy": opening, title: hint(view, conversation), onClick: () => onOpen(view), "data-skill": view.skill, children: [
1040
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "bda-preview-wrap", style: { display: "block", position: "relative", width: "100%" }, children: [
1041
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Preview, { view, thumbnailUrl }),
1042
+ opening && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "bda-busy", children: [
1043
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-spinner" }),
1044
+ t("card.opening")
1045
+ ] })
1046
+ ] }),
1047
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "bda-info", children: [
1048
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-glyph", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TypeIcon, { skill: view.skill }) }),
1049
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "bda-text", children: [
1050
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-name", children: view.title }),
1051
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-meta", children: ago(view.openedAt, now, t) })
1052
+ ] })
1053
+ ] })
1054
+ ] }),
1055
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bda-actions", children: [
1056
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MoreButton, { view, t, menuOpen, onMenu }),
1057
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PinButton, { view, t, onPin })
1058
+ ] })
1059
+ ] });
1060
+ }
1061
+ function Row({ view, state, conversation, now, t, onOpen, onPin, menuOpen, onMenu }) {
1062
+ const opening = state.opening === view.root;
1063
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: "bda-item", "data-menu": menuOpen, style: { "--bda-tint": tintOf(view.skill) }, children: [
1064
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "bda-row", disabled: state.opening !== null, "aria-busy": opening, title: hint(view, conversation), onClick: () => onOpen(view), "data-skill": view.skill, children: [
1065
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-glyph", children: opening ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-spinner" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TypeIcon, { skill: view.skill }) }),
1066
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-name", children: view.title }),
1067
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-meta", children: opening ? t("card.opening") : ago(view.openedAt, now, t) })
1068
+ ] }),
1069
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bda-row-tools", children: [
1070
+ view.shared && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-shared", title: t("card.shared"), "aria-label": t("card.shared"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { d: ICON.people }) }),
1071
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PinButton, { view, t, onPin }),
1072
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MoreButton, { view, t, menuOpen, onMenu, extra: "bda-act-more" })
1073
+ ] })
1074
+ ] });
1075
+ }
1076
+ function readView() {
1077
+ try {
1078
+ return localStorage.getItem(VIEW_KEY) === "list" ? "list" : "grid";
1079
+ } catch {
1080
+ return "grid";
1081
+ }
1082
+ }
1083
+ function writeView(view) {
1084
+ try {
1085
+ localStorage.setItem(VIEW_KEY, view);
1086
+ } catch {
1087
+ }
1088
+ }
1089
+ function ArtifactsPage(props) {
1090
+ const { t, artifactsStore, sessionsStore, reloadArtifacts, openArtifact, create, canCreate, createOptions, pin, rename, copyLink, remove, undo, thumbnailUrl, watchThumbnails, dismissToast } = props;
1091
+ const state = (0, import_react.useSyncExternalStore)(artifactsStore.subscribe, artifactsStore.getSnapshot);
1092
+ const sessions = (0, import_react.useSyncExternalStore)(sessionsStore.subscribe, sessionsStore.getSnapshot);
1093
+ const [query, setQuery] = (0, import_react.useState)("");
1094
+ const [searching, setSearching] = (0, import_react.useState)(false);
1095
+ const [filters, setFilters] = (0, import_react.useState)(NO_FILTERS);
1096
+ const [layout, setLayout] = (0, import_react.useState)(readView);
1097
+ const [popup, setPopup] = (0, import_react.useState)(null);
1098
+ const [dialog, setDialog] = (0, import_react.useState)(null);
1099
+ const searchBox = (0, import_react.useRef)(null);
1100
+ const now = useNow();
1101
+ (0, import_react.useEffect)(() => {
1102
+ reloadArtifacts();
1103
+ const again = () => {
1104
+ if (document.visibilityState === "visible") reloadArtifacts();
1105
+ };
1106
+ document.addEventListener("visibilitychange", again);
1107
+ return () => document.removeEventListener("visibilitychange", again);
1108
+ }, [reloadArtifacts]);
1109
+ (0, import_react.useEffect)(() => watchThumbnails(), [watchThumbnails]);
1110
+ (0, import_react.useEffect)(() => {
1111
+ if (searching) searchBox.current?.focus();
1112
+ }, [searching]);
1113
+ const counts = (0, import_react.useMemo)(() => {
1114
+ const by = /* @__PURE__ */ new Map();
1115
+ for (const a of state.artifacts) by.set(a.skill, (by.get(a.skill) ?? 0) + 1);
1116
+ return by;
1117
+ }, [state.artifacts]);
1118
+ const types = (0, import_react.useMemo)(() => SKILLS.filter((s) => state.skills.includes(s) || counts.has(s)), [state.skills, counts]);
1119
+ const tiles = (0, import_react.useMemo)(() => createOptions(), [state.skills, createOptions]);
1120
+ const shown = (0, import_react.useMemo)(() => {
1121
+ const q = query.trim().toLowerCase();
1122
+ const kept = state.artifacts.filter((a) => {
1123
+ if (filters.types.length > 0 && !filters.types.includes(a.skill)) return false;
1124
+ if (filters.pinned && !a.pinned) return false;
1125
+ if (filters.sharing !== "any" && a.shared !== (filters.sharing === "shared")) return false;
1126
+ if (!inRange(a.openedAt, filters.date, now)) return false;
1127
+ if (!q) return true;
1128
+ const conversation = sessions.byId?.[a.sessionId]?.title ?? "";
1129
+ return a.title.toLowerCase().includes(q) || conversation.toLowerCase().includes(q) || a.root.toLowerCase().includes(q);
1130
+ });
1131
+ return sorted(kept, filters.sort, t("time.locale"));
1132
+ }, [state.artifacts, filters, query, sessions, now, t]);
1133
+ const closePopup = (0, import_react.useMemo)(() => () => setPopup(null), []);
1134
+ const actionsFor = popup?.kind === "actions" ? state.artifacts.find((a) => a.root === popup.root) : void 0;
1135
+ const dialogFor = dialog ? state.artifacts.find((a) => a.root === dialog.root) : void 0;
1136
+ const itemProps = (view) => ({
1137
+ view,
1138
+ state,
1139
+ conversation: sessions.byId?.[view.sessionId]?.title?.trim() ?? "",
1140
+ now,
1141
+ t,
1142
+ thumbnailUrl,
1143
+ onOpen: openArtifact,
1144
+ onPin: (v) => pin(v, !v.pinned),
1145
+ menuOpen: popup?.kind === "actions" && popup.root === view.root,
1146
+ onMenu: (v, anchor) => setPopup((p) => p?.kind === "actions" && p.root === v.root ? null : { kind: "actions", anchor, root: v.root })
1147
+ });
1148
+ const toggle = (kind, anchor) => setPopup((p) => p?.kind === kind ? null : { kind, anchor });
1149
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "bda-page", "aria-label": t("title"), "data-testid": "baoyu-design-artifacts", children: [
1150
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "bda-scroll", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bda-content", children: [
1151
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bda-heading", children: [
1152
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { children: t("title") }),
1153
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bda-tools", children: [
1154
+ searching || query ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "bda-search", children: [
1155
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { d: ICON.search, size: 14 }),
1156
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1157
+ "input",
1158
+ {
1159
+ ref: searchBox,
1160
+ type: "search",
1161
+ value: query,
1162
+ placeholder: t("search"),
1163
+ "aria-label": t("search"),
1164
+ onChange: (e) => setQuery(e.target.value),
1165
+ onBlur: () => {
1166
+ if (!query) setSearching(false);
1167
+ },
1168
+ onKeyDown: (e) => {
1169
+ if (e.key === "Escape") {
1170
+ setQuery("");
1171
+ setSearching(false);
1172
+ }
1173
+ }
1174
+ }
1175
+ ),
1176
+ query && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1177
+ "button",
1178
+ {
1179
+ type: "button",
1180
+ "aria-label": t("search.clear"),
1181
+ onClick: () => {
1182
+ setQuery("");
1183
+ searchBox.current?.focus();
1184
+ },
1185
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { d: ICON.clear, size: 12 })
1186
+ }
1187
+ )
1188
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "bda-tool", "aria-label": t("search"), title: t("search"), onClick: () => setSearching(true), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { d: ICON.search, size: 18 }) }),
1189
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "bda-tool", "aria-label": t("filter.button"), title: t("filter.button"), "aria-haspopup": "menu", "aria-expanded": popup?.kind === "filter", "data-active": isFiltered(filters), onClick: (e) => toggle("filter", e.currentTarget), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { d: ICON.filter, size: 18 }) }),
1190
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1191
+ "button",
1192
+ {
1193
+ type: "button",
1194
+ className: "bda-tool",
1195
+ "aria-label": t(layout === "grid" ? "view.list" : "view.grid"),
1196
+ title: t(layout === "grid" ? "view.list" : "view.grid"),
1197
+ onClick: () => {
1198
+ const next = layout === "grid" ? "list" : "grid";
1199
+ setLayout(next);
1200
+ writeView(next);
1201
+ },
1202
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { d: layout === "grid" ? ICON.list : ICON.grid, size: 18 })
1203
+ }
1204
+ ),
1205
+ canCreate() && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "bda-tool", "aria-label": t("create.new"), title: t("create.new"), "aria-haspopup": "menu", "aria-expanded": popup?.kind === "create", onClick: (e) => toggle("create", e.currentTarget), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { d: ICON.plus, size: 18 }) })
1206
+ ] })
1207
+ ] }),
1208
+ tiles.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1209
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: "bda-section-title", children: t("create.heading") }),
1210
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "bda-tiles", children: tiles.map((skill) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "bda-tile", "data-skill": skill, style: { "--bda-tint": tintOf(skill) }, onClick: () => create(skill), children: [
1211
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "bda-tile-art", children: [
1212
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { className: "bda-tile-squiggle", viewBox: "0 0 140 100", preserveAspectRatio: "xMidYMid slice", fill: "none", stroke: "currentColor", strokeWidth: "1.1", strokeDasharray: "0 3.2", strokeLinecap: "round", "aria-hidden": "true", children: [
1213
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4 84C20 16 42 98 62 42s38 58 74-28" }),
1214
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M6 88C24 22 44 104 66 46s36 56 72-26" }),
1215
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M2 80C18 12 40 92 58 38s40 58 76-28" })
1216
+ ] }),
1217
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TypeIcon, { skill, size: 40 })
1218
+ ] }),
1219
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "bda-tile-label", children: t(`type.${skill}`) })
1220
+ ] }) }, skill)) })
1221
+ ] }),
1222
+ state.status === "loading" && state.artifacts.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "bda-grid bda-skeleton", "aria-hidden": "true", children: [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { className: "bda-item", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bda-card", children: [
1223
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "bda-preview" }),
1224
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "bda-line" })
1225
+ ] }) }, i)) }) : state.status === "error" && state.artifacts.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bda-empty", role: "alert", children: [
1226
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: t("load.error", { message: state.error }) }),
1227
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "bda-retry", onClick: reloadArtifacts, children: t("load.retry") })
1228
+ ] }) : state.artifacts.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bda-empty", children: [
1229
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArtifactsIcon, { size: 28 }),
1230
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { children: t("empty.title") }),
1231
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: t("empty.body") })
1232
+ ] }) : shown.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bda-empty", children: [
1233
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: t("empty.noMatches") }),
1234
+ isFiltered(filters) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "bda-retry", onClick: () => setFilters({ ...NO_FILTERS, sort: filters.sort }), children: t("filter.clear") })
1235
+ ] }) : layout === "grid" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "bda-grid", children: shown.map((view) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Card, { ...itemProps(view) }, view.root)) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "bda-list", children: shown.map((view) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Row, { ...itemProps(view) }, view.root)) })
1236
+ ] }) }),
1237
+ popup?.kind === "filter" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FilterMenu, { anchor: popup.anchor, filters, setFilters, types, counts, t, onClose: closePopup }),
1238
+ popup?.kind === "create" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1239
+ CreateMenu,
1240
+ {
1241
+ anchor: popup.anchor,
1242
+ skills: tiles,
1243
+ canCreate: canCreate(),
1244
+ t,
1245
+ onClose: closePopup,
1246
+ create: (skill) => {
1247
+ setPopup(null);
1248
+ create(skill);
1249
+ }
1250
+ }
1251
+ ),
1252
+ popup?.kind === "actions" && actionsFor && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1253
+ ActionsMenu,
1254
+ {
1255
+ anchor: popup.anchor,
1256
+ view: actionsFor,
1257
+ t,
1258
+ onClose: closePopup,
1259
+ act: (what) => {
1260
+ setPopup(null);
1261
+ if (what === "pin") pin(actionsFor, !actionsFor.pinned);
1262
+ else if (what === "copy") copyLink(actionsFor);
1263
+ else setDialog({ kind: what, root: actionsFor.root });
1264
+ }
1265
+ }
1266
+ ),
1267
+ dialog?.kind === "rename" && dialogFor && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1268
+ RenameDialog,
1269
+ {
1270
+ view: dialogFor,
1271
+ t,
1272
+ onClose: () => setDialog(null),
1273
+ onSave: (title) => {
1274
+ setDialog(null);
1275
+ rename(dialogFor, title);
1276
+ }
1277
+ }
1278
+ ),
1279
+ dialog?.kind === "delete" && dialogFor && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1280
+ DeleteDialog,
1281
+ {
1282
+ view: dialogFor,
1283
+ t,
1284
+ onClose: () => setDialog(null),
1285
+ onDelete: () => {
1286
+ setDialog(null);
1287
+ remove(dialogFor);
1288
+ }
1289
+ }
1290
+ ),
1291
+ state.toast && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ToastBar, { toast: state.toast, t, onClose: dismissToast, onUndo: undo }, state.toast.id)
1292
+ ] });
1293
+ }
1294
+ var SIDEBAR_WAIT_MS = 5e3;
1295
+ async function openInSidebar(sidebar, sessionId, url) {
1296
+ if (typeof sidebar.tabsIn !== "function") {
1297
+ sidebar.openTabIn(sessionId, "browser", { params: { url } });
1298
+ return true;
1299
+ }
1300
+ const browsers = () => sidebar.tabsIn(sessionId).filter((tab) => tab.kind === "browser").length;
1301
+ const deadline = Date.now() + SIDEBAR_WAIT_MS;
1302
+ for (; ; ) {
1303
+ const before = browsers();
1304
+ sidebar.openTabIn(sessionId, "browser", { params: { url } });
1305
+ if (browsers() > before) return true;
1306
+ if (Date.now() >= deadline) return false;
1307
+ await new Promise((resolve) => setTimeout(resolve, 100));
1308
+ }
1309
+ }
1310
+ var route = (path) => path.slice(1);
1311
+ function mountPage(ctx, h, self) {
1312
+ const disposers = [];
1313
+ const style = typeof document === "undefined" ? null : document.createElement("style");
1314
+ if (style) {
1315
+ style.dataset.pluginCss = "baoyu-design/artifacts-page.css";
1316
+ style.textContent = CSS;
1317
+ document.head.appendChild(style);
1318
+ disposers.push(() => style.remove());
1319
+ }
1320
+ disposers.push(ctx.locale.register(NS, { zh, en }));
1321
+ const t = ctx.locale.bind?.(NS) ?? ((key) => en[key] ?? key);
1322
+ const skillsOn = () => [...new Set([...h.members.values()].map((m) => m.skill))];
1323
+ const store = createStore({ status: "loading", artifacts: [], error: "", skills: skillsOn(), opening: null, toast: null });
1324
+ const onMembers = () => store.set({ skills: skillsOn() });
1325
+ h.listeners.add(onMembers);
1326
+ disposers.push(() => h.listeners.delete(onMembers));
1327
+ let toastId = 0;
1328
+ const toast = (message, kind = "info", undo2) => store.set({ toast: { id: ++toastId, message, kind, ...undo2 ? { undo: undo2 } : {} } });
1329
+ const failed = (error) => toast(t("action.failed", { message: error.message }), "error");
1330
+ const patchView = (root, patch) => store.set({ artifacts: store.getSnapshot().artifacts.map((a) => a.root === root ? { ...a, ...patch } : a) });
1331
+ const call = async (path, params, init = { method: "POST" }) => {
1332
+ const response = await fetch(`${route(path)}?${new URLSearchParams(params)}`, init);
1333
+ const body = await response.json().catch(() => ({}));
1334
+ if (!response.ok) throw new Error(typeof body.error === "string" ? body.error : `HTTP ${response.status}`);
1335
+ return body;
1336
+ };
1337
+ const ownPluginOn = (view) => {
1338
+ if (skillsOn().includes(view.skill)) return true;
1339
+ toast(t("open.pluginOff", { plugin: view.plugin }), "error");
1340
+ return false;
1341
+ };
1342
+ let pump = () => {
1343
+ };
1344
+ let loading = null;
1345
+ const reload = () => {
1346
+ if (loading) return;
1347
+ if (store.getSnapshot().artifacts.length === 0) store.set({ status: "loading" });
1348
+ const order = [self, ...skillsOn().filter((s) => s !== self)];
1349
+ loading = (async () => {
1350
+ let message = "";
1351
+ for (const skill of order) {
1352
+ try {
1353
+ const response = await fetch(route(listPath(skill)), { cache: "no-store" });
1354
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
1355
+ const body = await response.json();
1356
+ const artifacts = Array.isArray(body.artifacts) ? body.artifacts.filter((a) => a && typeof a.root === "string") : [];
1357
+ store.set({ status: "ready", artifacts, error: "" });
1358
+ pump();
1359
+ return;
1360
+ } catch (error) {
1361
+ message ||= error.message;
1362
+ }
1363
+ }
1364
+ store.set({ status: "error", error: message });
1365
+ })().finally(() => {
1366
+ loading = null;
1367
+ });
1368
+ };
1369
+ const open = (view) => {
1370
+ if (store.getSnapshot().opening !== null) return;
1371
+ if (!ownPluginOn(view)) return;
1372
+ store.set({ opening: view.root, toast: null });
1373
+ void (async () => {
1374
+ try {
1375
+ const response = await fetch(`${route(openPath(view.skill))}?${new URLSearchParams({ root: view.root })}`, { method: "POST" });
1376
+ const body = await response.json().catch(() => ({}));
1377
+ if (!response.ok || typeof body.url !== "string") throw new Error(typeof body.error === "string" ? body.error : `HTTP ${response.status}`);
1378
+ const url = body.url;
1379
+ const sessionId = typeof body.sessionId === "string" ? body.sessionId : view.sessionId;
1380
+ const sessions = ctx.get("sessions");
1381
+ const workspaces = ctx.get("workspaces");
1382
+ const workspace = ctx.get("uiWorkspace");
1383
+ const sidebar = ctx.get("sidebarRight");
1384
+ const tabs = ctx.get("sidebarRightTabs");
1385
+ const reachable = workspace !== void 0 && linkState(sessionId, sessions?.list?.getSnapshot(), workspaces?.list?.getSnapshot()) === "available";
1386
+ if (reachable) {
1387
+ workspace.openSession(sessionId);
1388
+ const shown = sidebar !== void 0 && tabs?.get("browser") !== void 0 && await openInSidebar(sidebar, sessionId, url);
1389
+ if (!shown) window.open(url, "_blank", "noopener,noreferrer");
1390
+ store.set({ opening: null });
1391
+ } else {
1392
+ window.open(url, "_blank", "noopener,noreferrer");
1393
+ store.set({ opening: null });
1394
+ toast(t("open.noSession"));
1395
+ }
1396
+ reload();
1397
+ } catch (error) {
1398
+ store.set({ opening: null });
1399
+ toast(t("open.failed", { message: error.message }), "error");
1400
+ }
1401
+ })();
1402
+ };
1403
+ const workspaceNav = () => ctx.get("uiWorkspace");
1404
+ const canCreate = () => typeof workspaceNav()?.startSession === "function";
1405
+ const createOptions = () => canCreate() ? creatable(skillsOn()) : [];
1406
+ const create = (skill) => {
1407
+ const nav = workspaceNav();
1408
+ if (typeof nav?.startSession !== "function") return;
1409
+ if (skill === void 0 || !isSkill(skill)) return nav.startSession();
1410
+ nav.startSession(void 0, { prompt: promptFor(skill, t), whenDraftExists: "append" });
1411
+ };
1412
+ const pin = (view, pinned) => {
1413
+ if (!ownPluginOn(view)) return;
1414
+ patchView(view.root, { pinned });
1415
+ call(updatePath(view.skill), { root: view.root }, { method: "POST", body: JSON.stringify({ pinned }) }).catch((error) => {
1416
+ patchView(view.root, { pinned: view.pinned });
1417
+ failed(error);
1418
+ });
1419
+ };
1420
+ const rename = (view, title) => {
1421
+ if (!ownPluginOn(view)) return;
1422
+ patchView(view.root, { title });
1423
+ call(updatePath(view.skill), { root: view.root }, { method: "POST", body: JSON.stringify({ title }) }).then(reload, (error) => {
1424
+ patchView(view.root, { title: view.title });
1425
+ failed(error);
1426
+ });
1427
+ };
1428
+ const copyText = async (text) => {
1429
+ try {
1430
+ await navigator.clipboard.writeText(text);
1431
+ } catch {
1432
+ const area = document.createElement("textarea");
1433
+ area.value = text;
1434
+ area.style.cssText = "position:fixed;opacity:0";
1435
+ document.body.append(area);
1436
+ area.select();
1437
+ const done = document.execCommand("copy");
1438
+ area.remove();
1439
+ if (!done) throw new Error("the clipboard refused it");
1440
+ }
1441
+ };
1442
+ const copyLink = (view) => {
1443
+ if (!ownPluginOn(view)) return;
1444
+ void (async () => {
1445
+ try {
1446
+ const { url } = await call(previewPath(view.skill), { root: view.root });
1447
+ await call(releasePath(view.skill), { root: view.root, keep: "1" });
1448
+ if (typeof url !== "string") throw new Error("no address");
1449
+ await copyText(url);
1450
+ toast(t("toast.copied"));
1451
+ } catch (error) {
1452
+ failed(error);
1453
+ }
1454
+ })();
1455
+ };
1456
+ const remove = (view) => {
1457
+ if (!ownPluginOn(view)) return;
1458
+ void (async () => {
1459
+ try {
1460
+ const { trash, moved } = await call(deletePath(view.skill), { root: view.root });
1461
+ store.set({ artifacts: store.getSnapshot().artifacts.filter((a) => a.root !== view.root) });
1462
+ undoVia.set(String(trash), view.skill);
1463
+ toast(t(moved === true ? "toast.deleted" : "toast.removed", { title: view.title }), "info", String(trash));
1464
+ } catch (error) {
1465
+ failed(error);
1466
+ }
1467
+ })();
1468
+ };
1469
+ const undoVia = /* @__PURE__ */ new Map();
1470
+ const undo = (id) => {
1471
+ store.set({ toast: null });
1472
+ call(restorePath(undoVia.get(id) ?? self), { trash: id }).then(reload, failed);
1473
+ };
1474
+ const thumbnailUrl = (view) => view.thumbnail === null ? null : `${route(thumbnailPath(self))}?${new URLSearchParams({ root: view.root, v: view.thumbnail })}`;
1475
+ const browser = desktopBrowser();
1476
+ const tried = /* @__PURE__ */ new Set();
1477
+ let watching = null;
1478
+ let taking = false;
1479
+ const takeOne = async (view, signal) => {
1480
+ const { url } = await call(previewPath(view.skill), { root: view.root });
1481
+ if (typeof url !== "string") throw new Error("no address");
1482
+ try {
1483
+ const bytes = await captureThumbnail(browser, url, view.skill, signal);
1484
+ const saved = await call(thumbnailPath(view.skill), { root: view.root }, { method: "POST", headers: { "content-type": "image/jpeg" }, body: new Uint8Array(bytes) });
1485
+ if (typeof saved.thumbnail === "string") patchView(view.root, { thumbnail: saved.thumbnail, updatedAt: saved.thumbnail });
1486
+ } finally {
1487
+ await call(releasePath(view.skill), { root: view.root }).catch(() => null);
1488
+ }
1489
+ };
1490
+ pump = () => {
1491
+ if (taking || watching === null || browser === void 0) return;
1492
+ const signal = watching.signal;
1493
+ taking = true;
1494
+ void (async () => {
1495
+ try {
1496
+ for (; ; ) {
1497
+ if (signal.aborted) return;
1498
+ if (document.visibilityState !== "visible") return;
1499
+ const on = skillsOn();
1500
+ const next = store.getSnapshot().artifacts.find((a) => a.thumbnail !== a.updatedAt && on.includes(a.skill) && !tried.has(`${a.root}\0${a.updatedAt}`));
1501
+ if (next === void 0) return;
1502
+ tried.add(`${next.root}\0${next.updatedAt}`);
1503
+ try {
1504
+ await takeOne(next, signal);
1505
+ } catch (error) {
1506
+ if (!signal.aborted) console.warn(`baoyu-design Artifacts page: no thumbnail for ${next.root}:`, error);
1507
+ }
1508
+ }
1509
+ } finally {
1510
+ taking = false;
1511
+ }
1512
+ })();
1513
+ };
1514
+ const watchThumbnails = () => {
1515
+ const controller = new AbortController();
1516
+ watching = controller;
1517
+ pump();
1518
+ return () => {
1519
+ controller.abort(new Error("the page closed"));
1520
+ if (watching === controller) watching = null;
1521
+ };
1522
+ };
1523
+ disposers.push(() => watching?.abort(new Error("the plugin stopped")));
1524
+ const sessionsStore = () => {
1525
+ const list = ctx.get("sessions")?.list;
1526
+ return isStore(list) ? list : EMPTY_SESSIONS;
1527
+ };
1528
+ disposers.push(toDisposer(ctx.slots.inject(
1529
+ "main",
1530
+ () => ctx.slots.register(
1531
+ {
1532
+ name: "main",
1533
+ key: PANEL_ID,
1534
+ locale: NS,
1535
+ inject: () => ({
1536
+ artifactsStore: store,
1537
+ sessionsStore: sessionsStore(),
1538
+ reloadArtifacts: reload,
1539
+ openArtifact: open,
1540
+ create,
1541
+ canCreate,
1542
+ createOptions,
1543
+ pin,
1544
+ rename,
1545
+ copyLink,
1546
+ remove,
1547
+ undo,
1548
+ thumbnailUrl,
1549
+ watchThumbnails,
1550
+ dismissToast: () => store.set({ toast: null })
1551
+ })
1552
+ },
1553
+ ArtifactsPage
1554
+ )
1555
+ )));
1556
+ disposers.push(toDisposer(ctx.slots.inject(
1557
+ "sidebar.panellist",
1558
+ () => ctx.slots.register(
1559
+ { name: "sidebar.panellist", id: PANEL_ID, order: 20, locale: NS, label: () => t("panel") },
1560
+ ArtifactsIcon
1561
+ )
1562
+ )));
1563
+ return () => {
1564
+ for (const dispose of disposers.splice(0).reverse()) {
1565
+ try {
1566
+ dispose();
1567
+ } catch (error) {
1568
+ console.error("baoyu-design Artifacts page: unregistering failed:", error);
1569
+ }
1570
+ }
1571
+ };
1572
+ }
1573
+ function toDisposer(value) {
1574
+ return typeof value === "function" ? value : () => {
1575
+ };
1576
+ }
1577
+ function joinArtifactsPage(ctx, plugin, skill) {
1578
+ const h = hub();
1579
+ h.members.set(plugin, { plugin, skill, version: PAGE_VERSION, mount: () => mountPage(ctx, h, skill) });
1580
+ settle(h);
1581
+ changed(h);
1582
+ ctx.effect(() => () => {
1583
+ h.members.delete(plugin);
1584
+ if (h.owner?.plugin === plugin) {
1585
+ const old = h.owner;
1586
+ h.owner = null;
1587
+ old.dispose();
1588
+ }
1589
+ settle(h);
1590
+ changed(h);
1591
+ }, `${plugin}: Artifacts page`);
1592
+ }
1593
+
1594
+ var import_jsx_runtime2 = require("react/jsx-runtime");
1595
+ var CLIENT = define_CLIENT_default;
1596
+ var COMMENT_SOURCE = "baoyu-design-comment";
1597
+ var KIND = `${CLIENT.key}:comment`;
1598
+ var NS2 = CLIENT.key;
1599
+ var zh2 = {
1600
+ "card.title": "已将对“{title}”的评论发给 Agent",
1601
+ "card.request": "已将“{title}”上的 {label} 发给 Agent",
1602
+ "card.view": "查看评论",
1603
+ "card.more": "显示位置",
1604
+ "card.less": "收起位置"
1605
+ };
1606
+ var en2 = {
1607
+ "card.title": "Comment on “{title}” sent to agent",
1608
+ "card.request": "{label} on “{title}” sent to agent",
1609
+ "card.view": "View thread",
1610
+ "card.more": "Show where",
1611
+ "card.less": "Hide where"
1612
+ };
1613
+ var str = (value) => typeof value === "string" ? value : "";
1614
+ function ours(event) {
1615
+ if (event.type !== "user/message" || event.surfaceOp !== "append") return false;
1616
+ const source = event.data?.source;
1617
+ return source?.kind === COMMENT_SOURCE && source.plugin === CLIENT.plugin;
1618
+ }
1619
+ function cardData(event) {
1620
+ const source = event.data?.source ?? {};
1621
+ const threads = Array.isArray(source.threads) ? source.threads : [];
1622
+ const requests = Array.isArray(source.requests) ? source.requests : [];
1623
+ return {
1624
+ title: str(source.title),
1625
+ url: str(source.url),
1626
+ threads: threads.map((t) => ({ id: str(t.id), where: str(t.where), text: str(t.text) })).filter((t) => t.id),
1627
+ requests: requests.map((r) => ({ id: str(r.id), label: str(r.label), text: str(r.text) })).filter((r) => r.id && r.text),
1628
+ seq: event.seq
1629
+ };
1630
+ }
1631
+ var definition = {
1632
+ kind: KIND,
1633
+ target: "chat",
1634
+ match: (event) => (
1635
+ // Claimed: the built-in input-message Definition would draw it as a context row.
1636
+ ours(event) ? { id: String(event.data?.id), role: "start", exclusive: true } : null
1637
+ ),
1638
+ start: (_context, match) => cardData(match.event),
1639
+ update: (context) => context.state,
1640
+ buildViewNode: (context) => {
1641
+ if (context.state === void 0) return null;
1642
+ return {
1643
+ key: context.key,
1644
+ kind: KIND,
1645
+ id: context.id,
1646
+ target: "chat",
1647
+ anchorSeq: context.state.seq,
1648
+ location: context.start?.location ?? context.matches[0]?.location ?? { kind: "unresolved" },
1649
+ visibility: "visible",
1650
+ // A person's input, as a user message: it opens the agent's reply below it.
1651
+ groupTree: "input",
1652
+ data: context.state
1653
+ };
1654
+ }
1655
+ };
1656
+ var CSS2 = `
1657
+ .bdc-row{display:flex;flex-direction:column;align-items:flex-end;gap:8px;width:100%}
1658
+ .bdc-card{box-sizing:border-box;max-width:min(calc(var(--dsh-chat-content-width, 748px) * 0.702), 82%);min-width:min(320px, 82%);
1659
+ border:0.5px solid var(--dsw-alias-border-l1);border-radius:var(--dsw-radius-xl, 12px);background:var(--dsw-alias-markdown-code-block);
1660
+ color:var(--dsw-alias-label-primary);padding:10px 14px 12px;display:flex;flex-direction:column;gap:6px}
1661
+ .bdc-head{display:flex;align-items:center;gap:6px;color:var(--dsw-alias-label-tertiary);font:var(--dsw-font-xxs-12, 12px/18px sans-serif)}
1662
+ .bdc-head svg{flex:none}
1663
+ .bdc-head-text{flex:1;min-width:0;display:flex;white-space:nowrap;overflow:hidden}
1664
+ .bdc-head-text>span{flex:none}
1665
+ .bdc-head-text>.bdc-head-title{flex:0 1 auto;min-width:1.5em;overflow:hidden;text-overflow:ellipsis}
1666
+ .bdc-toggle{flex:none;display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;margin:-2px -4px -2px 0;
1667
+ border:0;border-radius:var(--dsw-radius-sm, 6px);background:none;color:inherit;cursor:pointer;padding:0}
1668
+ .bdc-toggle:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}
1669
+ .bdc-toggle svg{transition:transform .15s}
1670
+ .bdc-toggle[aria-expanded=false] svg{transform:rotate(-90deg)}
1671
+ .bdc-body{font-size:var(--dsh-content-font-size, 14px);line-height:1.6;white-space:pre-wrap;overflow-wrap:anywhere}
1672
+ .bdc-meta{display:flex;flex-direction:column;gap:6px;padding-top:6px;border-top:0.5px solid var(--dsw-alias-border-l1)}
1673
+ .bdc-where{color:var(--dsw-alias-label-tertiary);font:var(--dsw-font-xxs-12, 12px/18px sans-serif);overflow-wrap:anywhere}
1674
+ .bdc-view{align-self:flex-start;display:inline-flex;align-items:center;gap:2px;height:24px;margin-left:-6px;padding:0 6px;border:0;
1675
+ border-radius:var(--dsw-radius-sm, 6px);background:none;color:var(--dsw-alias-label-secondary);font:var(--dsw-font-xxs-12, 12px/18px sans-serif);cursor:pointer}
1676
+ .bdc-view:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}
1677
+ .bdc-view:focus-visible,.bdc-toggle:focus-visible{outline:var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));outline-offset:1px}
1678
+ @media (prefers-reduced-motion:reduce){.bdc-toggle svg{transition:none}}
1679
+ `;
1680
+ var iconStyle = { display: "block" };
1681
+ var TITLE_MARK = "";
1682
+ function CommentIcon() {
1683
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", style: iconStyle, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1684
+ "path",
1685
+ {
1686
+ d: "M3 3.5h10a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H8.2L5.4 13.8a.3.3 0 0 1-.5-.23V11.5H3a1 1 0 0 1-1-1v-6a1 1 0 0 1 1-1Z",
1687
+ stroke: "currentColor",
1688
+ strokeWidth: "1.2",
1689
+ strokeLinejoin: "round"
1690
+ }
1691
+ ) });
1692
+ }
1693
+ function RequestIcon() {
1694
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", style: iconStyle, children: [
1695
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "2", y: "4", width: "12", height: "7", rx: "2", stroke: "currentColor", strokeWidth: "1.2" }),
1696
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8.5 8.5 11 14l.9-2.1L14 11l-5.5-2.5Z", fill: "currentColor", stroke: "var(--dsw-alias-markdown-code-block, #fff)", strokeWidth: "0.8", strokeLinejoin: "round" })
1697
+ ] });
1698
+ }
1699
+ function ChevronDown() {
1700
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", "aria-hidden": "true", style: iconStyle, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3 4.5 6 7.5l3-3", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round", strokeLinejoin: "round" }) });
1701
+ }
1702
+ function ChevronRight() {
1703
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", "aria-hidden": "true", style: iconStyle, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M4.5 3 7.5 6l-3 3", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round", strokeLinejoin: "round" }) });
1704
+ }
1705
+ function Card2({ title, thread, url, t, openThread }) {
1706
+ const [open, setOpen] = (0, import_react2.useState)(true);
1707
+ const heading = t("card.title", { title });
1708
+ const meta = !CLIENT.simple && (thread.where || url);
1709
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "bdc-card", "data-comment-id": thread.id, children: [
1710
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "bdc-head", children: [
1711
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CommentIcon, {}),
1712
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Heading, { text: heading, title, parts: t("card.title", { title: TITLE_MARK }).split(TITLE_MARK) }),
1713
+ meta && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "bdc-toggle", "aria-expanded": open, "aria-label": t(open ? "card.less" : "card.more"), onClick: () => setOpen(!open), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ChevronDown, {}) })
1714
+ ] }),
1715
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "bdc-body", children: thread.text }),
1716
+ meta && open && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "bdc-meta", children: [
1717
+ thread.where && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "bdc-where", children: thread.where }),
1718
+ url && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { type: "button", className: "bdc-view", onClick: () => openThread(url, thread.id), children: [
1719
+ t("card.view"),
1720
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ChevronRight, {})
1721
+ ] })
1722
+ ] })
1723
+ ] });
1724
+ }
1725
+ function Heading({ text, title, parts }) {
1726
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "bdc-head-text", title: text, children: parts.length === 2 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
1727
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: parts[0] }),
1728
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "bdc-head-title", children: title }),
1729
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: parts[1] })
1730
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "bdc-head-title", children: text }) });
1731
+ }
1732
+ function RequestCardView({ title, request, t }) {
1733
+ const label = request.label || "Request";
1734
+ const heading = t("card.request", { title, label });
1735
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "bdc-card", "data-request-id": request.id, children: [
1736
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "bdc-head", children: [
1737
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RequestIcon, {}),
1738
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Heading, { text: heading, title, parts: t("card.request", { title: TITLE_MARK, label }).split(TITLE_MARK) })
1739
+ ] }),
1740
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "bdc-body", children: request.text })
1741
+ ] });
1742
+ }
1743
+ function CommentCards({ node, t, openThread }) {
1744
+ const { title, url, threads, requests } = node.data;
1745
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "bdc-row", children: [
1746
+ threads.map((thread) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Card2, { title, thread, url, t, openThread }, thread.id)),
1747
+ (requests ?? []).map((request) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RequestCardView, { title, request, t }, request.id))
1748
+ ] });
1749
+ }
1750
+ var inject = ["slots", "locale", "uiConversation"];
1751
+ function threadUrl(url, id) {
1752
+ try {
1753
+ const at = new URL(url);
1754
+ at.searchParams.set("comment", id);
1755
+ return at.href;
1756
+ } catch {
1757
+ return url;
1758
+ }
1759
+ }
1760
+ function apply(ctx) {
1761
+ ctx.effect(() => {
1762
+ if (typeof document === "undefined") return () => {
1763
+ };
1764
+ const id = `${CLIENT.plugin}/comment-card.css`;
1765
+ if (document.querySelector(`style[data-plugin-css=${JSON.stringify(id)}]`)) return () => {
1766
+ };
1767
+ const tag = document.createElement("style");
1768
+ tag.dataset.plugin = CLIENT.plugin;
1769
+ tag.dataset.pluginCss = id;
1770
+ tag.textContent = CSS2;
1771
+ document.head.appendChild(tag);
1772
+ return () => tag.remove();
1773
+ }, `${CLIENT.plugin}: comment card styles`);
1774
+ ctx.effect(() => ctx.locale.register(NS2, { zh: zh2, en: en2 }), `${CLIENT.plugin}: dictionaries`);
1775
+ ctx.uiConversation.events.register(definition);
1776
+ ctx.slots.inject(
1777
+ "conversation.chat.node",
1778
+ () => ctx.slots.register(
1779
+ {
1780
+ name: "conversation.chat.node",
1781
+ key: KIND,
1782
+ locale: NS2,
1783
+ inject: (sessionId) => ({
1784
+ openThread: (url, threadId) => {
1785
+ const target = threadUrl(url, threadId);
1786
+ const sidebar = ctx.get("sidebarRight");
1787
+ const tabs = ctx.get("sidebarRightTabs");
1788
+ if (sidebar && tabs?.get("browser") !== void 0) sidebar.openTabIn(sessionId, "browser", { params: { url: target } });
1789
+ else window.open(target, "_blank", "noopener,noreferrer");
1790
+ }
1791
+ })
1792
+ },
1793
+ CommentCards
1794
+ )
1795
+ );
1796
+ joinArtifactsPage(ctx, CLIENT.plugin, CLIENT.skill);
1797
+ }
1798
+ return module.exports;
1799
+ }
1800
+ });