dsh-comfyui 0.3.0-beta.4 → 0.3.0-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.en.md CHANGED
@@ -22,7 +22,7 @@
22
22
 
23
23
  - `comfyui_run` — submit a ComfyUI API-format workflow, or pick a built-in template, and get the generated media back. Two modes: `sync` (wait and return media) and `async` (start a background job, collect with `job_output` — ideal for video).
24
24
  - `comfyui_object_info` — list the node definitions of your ComfyUI server so the agent can build valid workflows on the fly.
25
- - `comfyui_workflow` — list and run runnable workflows from the plugin library. `action: list` also reports graph workflows you saved on the ComfyUI server and whether each has been **extracted** into runnable workflows; unextracted graphs are flagged so the agent tells you to extract them in the panel first.
25
+ - `comfyui_workflow` — list and run runnable workflows from the plugin library. `action: list` also reports your ComfyUI server address and local install dirs (the `env` field), plus graph workflows you saved on the ComfyUI server and whether each has been **extracted** into runnable workflows; unextracted graphs are flagged so the agent tells you to extract them in the panel first. `action: refresh` re-derives one workflow's parameter snapshot by id and saves it back (force-rescans the TTS voice library first, then updates option/number metadata from the current node definitions) — run it after the voice library or node definitions changed.
26
26
 
27
27
  ### UI panel
28
28
 
@@ -66,13 +66,13 @@ Results render as a media wall (images/videos with download links) right in the
66
66
 
67
67
  ### Settings page
68
68
 
69
- A ComfyUI section in the DH settings where you can edit the server URL (`baseUrl`), API-key env var (`apiKeyEnv`), media base URL (`mediaHost`), test the connection, and switch the plugin UI language (Chinese / English — stored in the browser, applies to the whole plugin UI), all without touching `cordis.yml`. The data directory and asset cap are configured in `cordis.yml` only and are not exposed in the settings page.
69
+ A ComfyUI section in the DH settings where you can edit the server URL (`baseUrl`), API-key env var (`apiKeyEnv`), media base URL (`mediaHost`), the local ComfyUI install directories (`comfyuiDirs`, multiple allowed — mapped dirs / multiple installs; the agent uses them to locate your ComfyUI files and the TTS voice library without asking), test the connection, and switch the plugin UI language (Chinese / English — stored in the browser, applies to the whole plugin UI), all without touching `cordis.yml`. The data directory and asset cap are configured in `cordis.yml` only and are not exposed in the settings page.
70
70
 
71
71
  <p align="center"><img src="images/settings.png" width="70%" alt="ComfyUI settings page (with UI language switch)" title="ComfyUI settings page (with UI language switch)" /></p>
72
72
 
73
73
  ### Companion skill
74
74
 
75
- A runtime skill (`dsh-comfyui-workflows`) registered through `ctx.skills.register`: the agent learns the graph-vs-runnable model, canvas analysis rules (connected components, bypassed groups, dangling nodes), when to ask you about extract mode, and the extraction tech rules.
75
+ A runtime skill (`dsh-comfyui-workflows`) registered through `ctx.skills.register`: the agent learns the graph-vs-runnable model, canvas analysis rules (connected components, bypassed groups, dangling nodes), when to ask you about extract mode, and the extraction tech rules. The skill also carries the **local environment** story (read `env.comfyuiDirs` from `comfyui_workflow list`) and the **TTS-Audio-Suite voice-library workflow** (one unified flow: refresh the snapshot first, then query — refresh via the `/api/tts-audio-suite/voice-library?refresh=1` endpoint plus `action: refresh` to re-derive and write the snapshot back; query prefers the HTTP endpoint, otherwise lists `{comfyuiDir}/models/voices` etc.; a new voice rejected at run time = stale snapshot), so the agent never has to dig through the plugin's source again.
76
76
 
77
77
  ### Graph workflows vs runnable workflows
78
78
 
@@ -98,6 +98,7 @@ Every runnable workflow carries an adjustable **parameter set** (`parameters`) s
98
98
  - **Boolean parameters use a checkbox**: pick `true` / `false` directly; at run time the `"true"` / `"false"` / `0` / `1` spellings are accepted too (defaults stored as strings by older versions are normalized instead of being silently dropped).
99
99
  - **Int vs. float number parameters**: the declared input type (`INT` / `FLOAT`) is read from the ComfyUI node definition, so FLOAT inputs like `cfg` or `denoise` accept decimals while INT inputs like `steps` or `seed` are rounded at run time; unknown types are treated as decimals. Each row shows `number/int` or `number/float`, with range and step in the tooltip.
100
100
  - **Agent-facing**: the parameter list is written into the workflow's input notes (`inputs` field) and shown by `comfyui_workflow` `action: list`; `action: run` accepts `parameters: {"prompt": "...", "seed": 42}` overrides — explicit values win over random/default, omitted parameters use their defaults.
101
+ - **Parameter snapshot & refresh**: a parameter's options/number metadata (`options`, `numberKind`, …) is copied from the ComfyUI node definitions **at save time** and is never auto-updated afterwards (noticeable with growing lists like the voice library or uploaded files). Refresh it via `comfyui_workflow action: refresh { id }`, or — from other programs — the same-origin route `POST /comfyui/workflows/refresh-params` (body `{ "id": ... }`). Refreshing only touches derived fields; your hand-added advanced parameters and authored defaults are preserved. Until refreshed, `action: list` may be missing new voices and `action: run` will reject them.
101
102
  - **Load-area integration**: an unset image-type parameter is filled with the load-area's current source image, and unset `width`/`height` are auto-matched to that image's recorded pixel size. Explicit values always win, so the agent can still override both.
102
103
 
103
104
  ## Requirements
@@ -155,6 +156,8 @@ The plugin reads a `comfyui` section from `cordis.yml` (or the settings page):
155
156
  maxAssets: 200
156
157
  mediaHost: ''
157
158
  outputDir: ''
159
+ comfyuiDirs:
160
+ - 'D:\ComfyUI'
158
161
  ```
159
162
 
160
163
  | Key | Default | Description |
@@ -170,6 +173,7 @@ The plugin reads a `comfyui` section from `cordis.yml` (or the settings page):
170
173
  | `maxAssets` | `200` | Max entries kept in the asset index |
171
174
  | `mediaHost` | `''` (auto-detect) | External base URL for generated media (e.g. `http://192.168.1.5:3080`); empty auto-uses the origin the browser actually reaches this server with |
172
175
  | `outputDir` | `''` (inferred) | ComfyUI's output directory on this machine, used to locate files when deleting an asset; empty infers it from the paths ComfyUI reports, and deletion falls back to removing the index record when it cannot (e.g. a remote ComfyUI) |
176
+ | `comfyuiDirs` | `[]` | Local ComfyUI install root(s) (multiple allowed: mapped dirs / multiple installs). The agent reads them via `env.comfyuiDirs` from `comfyui_workflow list` to locate models, custom nodes and the TTS-Audio-Suite voice library directly |
173
177
 
174
178
  ## Roadmap & design boundaries
175
179
 
package/README.md CHANGED
@@ -22,7 +22,7 @@
22
22
 
23
23
  - `comfyui_run` —— 提交 ComfyUI API 格式的工作流,或选用内置模板,返回生成的媒体。两种模式:`sync`(等待并返回媒体)与 `async`(后台任务,用 `job_output` 收集结果——视频生成强烈建议)。
24
24
  - `comfyui_object_info` —— 列出你的 ComfyUI 服务器支持的节点定义,让 Agent 能当场构造合法的工作流。
25
- - `comfyui_workflow` —— 列出并运行插件库中的可运行工作流。`action: list` 还会报告你在 ComfyUI 端保存的图工作流以及每个是否已**提取**出可运行执行流;未提取的会明确标注,Agent 会先转告你在面板里点"提取"
25
+ - `comfyui_workflow` —— 列出并运行插件库中的可运行工作流。`action: list` 还会报告你的 ComfyUI 服务器地址与本机 ComfyUI 目录(`env` 字段),以及你在 ComfyUI 端保存的图工作流和每个是否已**提取**出可运行执行流;未提取的会明确标注,Agent 会先转告你在面板里点"提取"。`action: refresh` 按 id 重算某个工作流的参数快照并写回(先强制 TTS 音色库重扫,再按最新节点定义更新参数的选项/数值声明)——音色库或节点定义变更后跑它。
26
26
 
27
27
  ### UI 面板
28
28
 
@@ -66,13 +66,13 @@
66
66
 
67
67
  ### 设置页
68
68
 
69
- DH 设置里新增 "ComfyUI" 分区:改服务器地址(`baseUrl`)、API Key 环境变量名(`apiKeyEnv`)、媒体访问地址(`mediaHost`)、测试连接,并可切换插件界面语言(中文 / English——存于浏览器,作用于整个插件 UI),无需改动 `cordis.yml`。数据目录与资产上限只通过 `cordis.yml` 配置,不在设置页暴露。
69
+ DH 设置里新增 "ComfyUI" 分区:改服务器地址(`baseUrl`)、API Key 环境变量名(`apiKeyEnv`)、媒体访问地址(`mediaHost`)、本机 ComfyUI 安装目录(`comfyuiDirs`,可多条——目录映射 / 多实例;Agent 据此直接定位你的 ComfyUI 文件与 TTS 音色库,无需再询问)、测试连接,并可切换插件界面语言(中文 / English——存于浏览器,作用于整个插件 UI),无需改动 `cordis.yml`。数据目录与资产上限只通过 `cordis.yml` 配置,不在设置页暴露。
70
70
 
71
71
  <p align="center"><img src="images/settings.png" width="70%" alt="ComfyUI 设置页(含界面语言切换)" title="ComfyUI 设置页(含界面语言切换)" /></p>
72
72
 
73
73
  ### 配套 skill
74
74
 
75
- 通过 `ctx.skills.register` 注册的运行时 skill(`dsh-comfyui-workflows`):让 Agent 掌握图工作流 vs 执行流的概念、画布分析规则(连通分量、绕过组、悬空节点)、何时该询问你提取方式,以及图→API 提取的技术规则。
75
+ 通过 `ctx.skills.register` 注册的运行时 skill(`dsh-comfyui-workflows`):让 Agent 掌握图工作流 vs 执行流的概念、画布分析规则(连通分量、绕过组、悬空节点)、何时该询问你提取方式,以及图→API 提取的技术规则。skill 还附带了**本机环境**说明(从 `comfyui_workflow list` 的 `env` 读你的 ComfyUI 目录)与**TTS-Audio-Suite 音色库查询**(统一流程:先刷新快照再查询——刷新用 ComfyUI 的 `/api/tts-audio-suite/voice-library?refresh=1` + `action: refresh` 重算写回快照,查询优先走 HTTP 接口、其次直接列 `{comfyuiDir}/models/voices` 等目录;新音色运行被拒 = 快照过期),Agent 不必反复翻插件源码。
76
76
 
77
77
  ### 图工作流 vs 可运行工作流(提取执行流)
78
78
 
@@ -98,6 +98,7 @@ ComfyUI 分两层:
98
98
  - **布尔参数用勾选框编辑**:`true` / `false` 直接点选;运行时也接受 `"true"` / `"false"` / `0` / `1` 这些写法(旧版本存成字符串的默认值会自动归一,不再被静默忽略)。
99
99
  - **数字参数区分整数/小数**:从 ComfyUI 节点定义读取输入的声明类型(`INT` / `FLOAT`),`cfg`、`denoise` 这类 FLOAT 参数可直接填小数,`steps`、`seed` 这类 INT 参数在运行时四舍五入;类型未知时按小数处理。参数行会标出 `number/int`、`number/float`,鼠标悬停显示取值范围与步长。
100
100
  - **Agent 感知**:参数清单自动写入工作流的"参数说明"(`inputs` 字段),`comfyui_workflow` 工具的 `action: list` 会展示;`action: run` 接受 `parameters: {"prompt": "...", "seed": 42}` 覆盖——显式传值优先于随机/默认,未传参数用默认值。
101
+ - **参数快照与刷新**:每个参数的选项/数值声明(`options`、`numberKind` 等)在**保存工作流那一刻**从 ComfyUI 节点定义拷贝,之后不会自动更新(音色库、上传文件这类会长大的列表尤其明显)。刷新手段:Agent 用 `comfyui_workflow action: refresh { id }`;其他程序可调同源路由 `POST /comfyui/workflows/refresh-params`(body `{ "id": ... }`)。刷新只更新派生字段,**不会**动你手动加的高级参数与默认值;不刷新时,`action: list` 的选项可能缺新音色、`action: run` 传新音色会被拒绝。
101
102
  - **加载区联动**:未显式指定的加载参数按加载位顺序自动填入(同类型匹配);未传的 `width`/`height` 自动匹配源图记录的像素尺寸。显式传值始终优先,Agent 仍可覆盖两者,并能从 `action: list` 的 `loadArea` 字段看到用户加载了什么。
102
103
 
103
104
  ## 环境要求
@@ -155,6 +156,8 @@ Agent 会选用模板,或用 `comfyui_object_info` 探查你的服务器,或
155
156
  maxAssets: 200
156
157
  mediaHost: ''
157
158
  outputDir: ''
159
+ comfyuiDirs:
160
+ - 'D:\ComfyUI'
158
161
  ```
159
162
 
160
163
  | 键 | 默认值 | 说明 |
@@ -170,6 +173,7 @@ Agent 会选用模板,或用 `comfyui_object_info` 探查你的服务器,或
170
173
  | `maxAssets` | `200` | 资产索引最多保留的条数 |
171
174
  | `mediaHost` | `''`(自动检测) | 生成媒体的外网访问基址(如 `http://192.168.1.5:3080`);留空自动使用浏览器实际访问本服务器的地址 |
172
175
  | `outputDir` | `''`(自动推断) | ComfyUI 在本机的输出目录,删除资产时用它定位文件;留空则从 ComfyUI 返回的文件路径自动推断,推断不出(如远程部署)时只删索引记录 |
176
+ | `comfyuiDirs` | `[]` | 本机 ComfyUI 安装目录列表(可多条:目录映射 / 多实例)。Agent 通过 `comfyui_workflow list` 的 `env.comfyuiDirs` 读取,据此定位 models、自定义节点与 TTS-Audio-Suite 音色库等文件 |
173
177
 
174
178
  ## Roadmap 与设计边界
175
179
 
package/client/client.js CHANGED
@@ -26,6 +26,11 @@ window.__ModuleLoader__.load({
26
26
  configNotWritable: "设置服务不可用,请通过 cordis.yml 修改配置。",
27
27
  mediaHost: "媒体访问地址",
28
28
  mediaHostHint: "生成图片/媒体在聊天中使用的完整地址(如 http://192.168.1.5:3080)。留空时自动使用浏览器访问本服务器的地址;内网或反向代理下若图片不显示,请在此填写你访问本页面所用的地址。",
29
+ comfyuiDirs: "ComfyUI 目录",
30
+ comfyuiDirsHint: "本机 ComfyUI 安装目录,可添加多个(目录映射 / 多实例 / 便携版)。Agent 会据此直接定位你的 ComfyUI 文件(models、工作流、TTS-Audio-Suite 音色库等),无需再向你询问。",
31
+ comfyuiDirAdd: "+ 添加目录",
32
+ comfyuiDirRemove: "移除该目录",
33
+ comfyuiDirPlaceholder: "如 D:\\ComfyUI",
29
34
  language: "界面语言",
30
35
  languageHint: "切换插件界面语言(中文 / English)。切换后页面会自动刷新。",
31
36
  cardGenerating: "ComfyUI 生成中…",
@@ -202,6 +207,11 @@ window.__ModuleLoader__.load({
202
207
  configNotWritable: "Settings service unavailable — edit cordis.yml instead.",
203
208
  mediaHost: "Media base URL",
204
209
  mediaHostHint: "Absolute base URL used for generated media in chat (e.g. http://192.168.1.5:3080). Leave empty to auto-detect the address your browser uses to reach this server; fill it in when images do not load over LAN or a reverse proxy.",
210
+ comfyuiDirs: "ComfyUI directories",
211
+ comfyuiDirsHint: "ComfyUI install root(s) on this machine; add multiple for mapped/multiple installs. The agent reads these to locate your ComfyUI files (models, workflows, the TTS-Audio-Suite voice library) directly instead of asking you.",
212
+ comfyuiDirAdd: "+ Add directory",
213
+ comfyuiDirRemove: "Remove this directory",
214
+ comfyuiDirPlaceholder: "e.g. D:\\ComfyUI",
205
215
  language: "Language",
206
216
  languageHint: "Switch the plugin UI language (中文 / English). The page reloads after switching.",
207
217
  cardGenerating: "ComfyUI generating…",
@@ -694,6 +704,7 @@ window.__ModuleLoader__.load({
694
704
  const [baseUrl, setBaseUrl] = (0, react.useState)("");
695
705
  const [apiKeyEnv, setApiKeyEnv] = (0, react.useState)("");
696
706
  const [mediaHost, setMediaHost] = (0, react.useState)("");
707
+ const [comfyuiDirs, setComfyuiDirs] = (0, react.useState)([]);
697
708
  const [loadError, setLoadError] = (0, react.useState)(null);
698
709
  const [saving, setSaving] = (0, react.useState)(false);
699
710
  const [saveState, setSaveState] = (0, react.useState)("idle");
@@ -708,6 +719,7 @@ window.__ModuleLoader__.load({
708
719
  setBaseUrl(view.baseUrl);
709
720
  setApiKeyEnv(view.apiKeyEnv);
710
721
  setMediaHost(view.mediaHost ?? "");
722
+ setComfyuiDirs(Array.isArray(view.comfyuiDirs) ? view.comfyuiDirs : []);
711
723
  }).catch((error) => {
712
724
  if (cancelled) return;
713
725
  setLoadError(error instanceof Error ? error.message : String(error));
@@ -720,16 +732,19 @@ window.__ModuleLoader__.load({
720
732
  setSaving(true);
721
733
  setSaveState("idle");
722
734
  try {
735
+ const dirs = comfyuiDirs.map((dir) => dir.trim()).filter((dir) => dir !== "");
723
736
  const payload = await postJson("/comfyui/config", { patch: {
724
737
  baseUrl,
725
738
  apiKeyEnv,
726
- mediaHost
739
+ mediaHost,
740
+ comfyuiDirs: dirs
727
741
  } });
728
742
  if (payload.config !== void 0) {
729
743
  setConfig(payload.config);
730
744
  setBaseUrl(payload.config.baseUrl);
731
745
  setApiKeyEnv(payload.config.apiKeyEnv);
732
746
  setMediaHost(payload.config.mediaHost ?? "");
747
+ setComfyuiDirs(payload.config.comfyuiDirs ?? []);
733
748
  }
734
749
  setSaveState("saved");
735
750
  } catch (error) {
@@ -799,7 +814,43 @@ window.__ModuleLoader__.load({
799
814
  setMediaHost(event.target.value);
800
815
  setSaveState("idle");
801
816
  }
802
- }), (0, react.createElement)("div", { className: "dsc-hint" }, t("mediaHostHint"))), (0, react.createElement)("div", { className: "dsc-row" }, (0, react.createElement)("button", {
817
+ }), (0, react.createElement)("div", { className: "dsc-hint" }, t("mediaHostHint"))), (0, react.createElement)("div", { className: "dsc-field" }, (0, react.createElement)("label", null, t("comfyuiDirs")), comfyuiDirs.map((dir, index) => (0, react.createElement)("div", {
818
+ key: `dsc-dir-${index}`,
819
+ style: {
820
+ display: "flex",
821
+ gap: "8px",
822
+ marginBottom: "8px",
823
+ alignItems: "center"
824
+ }
825
+ }, (0, react.createElement)("input", {
826
+ className: "dsc-input",
827
+ value: dir,
828
+ placeholder: t("comfyuiDirPlaceholder"),
829
+ style: { flex: "1" },
830
+ onChange: (event) => {
831
+ const next = comfyuiDirs.slice();
832
+ next[index] = event.target.value;
833
+ setComfyuiDirs(next);
834
+ setSaveState("idle");
835
+ }
836
+ }), (0, react.createElement)("button", {
837
+ className: "dsc-btn",
838
+ title: t("comfyuiDirRemove"),
839
+ disabled: !config.writable,
840
+ onClick: () => {
841
+ const next = comfyuiDirs.slice();
842
+ next.splice(index, 1);
843
+ setComfyuiDirs(next);
844
+ setSaveState("idle");
845
+ }
846
+ }, "×"))), (0, react.createElement)("button", {
847
+ className: "dsc-btn",
848
+ disabled: !config.writable,
849
+ onClick: () => {
850
+ setComfyuiDirs([...comfyuiDirs, ""]);
851
+ setSaveState("idle");
852
+ }
853
+ }, t("comfyuiDirAdd")), (0, react.createElement)("div", { className: "dsc-hint" }, t("comfyuiDirsHint"))), (0, react.createElement)("div", { className: "dsc-row" }, (0, react.createElement)("button", {
803
854
  className: "dsc-btn",
804
855
  disabled: saving || !config.writable,
805
856
  onClick: () => void save()
@@ -2804,7 +2855,123 @@ window.__ModuleLoader__.load({
2804
2855
  controls: true,
2805
2856
  preload: "metadata",
2806
2857
  onClick: (event) => event.stopPropagation()
2807
- }) : (0, react.createElement)("img", {
2858
+ }) : first.kind === "audio" ? (0, react.createElement)("div", { className: "dsc-asset-audio-icon" }, (0, react.createElement)("svg", {
2859
+ className: "dsc-asset-audio-icon-wave",
2860
+ "aria-hidden": "true"
2861
+ }, (0, react.createElement)("defs", null, (0, react.createElement)("pattern", {
2862
+ id: "dsc-wave-pattern",
2863
+ width: 64,
2864
+ height: 110,
2865
+ patternUnits: "userSpaceOnUse"
2866
+ }, (0, react.createElement)("g", { fill: "rgba(255,255,255,0.15)" }, (0, react.createElement)("rect", {
2867
+ x: 0,
2868
+ y: 50,
2869
+ width: 2,
2870
+ height: 10,
2871
+ rx: 1
2872
+ }), (0, react.createElement)("rect", {
2873
+ x: 4,
2874
+ y: 44,
2875
+ width: 2,
2876
+ height: 22,
2877
+ rx: 1
2878
+ }), (0, react.createElement)("rect", {
2879
+ x: 8,
2880
+ y: 48,
2881
+ width: 2,
2882
+ height: 14,
2883
+ rx: 1
2884
+ }), (0, react.createElement)("rect", {
2885
+ x: 12,
2886
+ y: 38,
2887
+ width: 2,
2888
+ height: 34,
2889
+ rx: 1
2890
+ }), (0, react.createElement)("rect", {
2891
+ x: 16,
2892
+ y: 46,
2893
+ width: 2,
2894
+ height: 18,
2895
+ rx: 1
2896
+ }), (0, react.createElement)("rect", {
2897
+ x: 20,
2898
+ y: 52,
2899
+ width: 2,
2900
+ height: 6,
2901
+ rx: 1
2902
+ }), (0, react.createElement)("rect", {
2903
+ x: 24,
2904
+ y: 40,
2905
+ width: 2,
2906
+ height: 30,
2907
+ rx: 1
2908
+ }), (0, react.createElement)("rect", {
2909
+ x: 28,
2910
+ y: 48,
2911
+ width: 2,
2912
+ height: 14,
2913
+ rx: 1
2914
+ }), (0, react.createElement)("rect", {
2915
+ x: 32,
2916
+ y: 42,
2917
+ width: 2,
2918
+ height: 26,
2919
+ rx: 1
2920
+ }), (0, react.createElement)("rect", {
2921
+ x: 36,
2922
+ y: 36,
2923
+ width: 2,
2924
+ height: 38,
2925
+ rx: 1
2926
+ }), (0, react.createElement)("rect", {
2927
+ x: 40,
2928
+ y: 47,
2929
+ width: 2,
2930
+ height: 16,
2931
+ rx: 1
2932
+ }), (0, react.createElement)("rect", {
2933
+ x: 44,
2934
+ y: 51,
2935
+ width: 2,
2936
+ height: 8,
2937
+ rx: 1
2938
+ }), (0, react.createElement)("rect", {
2939
+ x: 48,
2940
+ y: 40,
2941
+ width: 2,
2942
+ height: 30,
2943
+ rx: 1
2944
+ }), (0, react.createElement)("rect", {
2945
+ x: 52,
2946
+ y: 45,
2947
+ width: 2,
2948
+ height: 20,
2949
+ rx: 1
2950
+ }), (0, react.createElement)("rect", {
2951
+ x: 56,
2952
+ y: 38,
2953
+ width: 2,
2954
+ height: 34,
2955
+ rx: 1
2956
+ }), (0, react.createElement)("rect", {
2957
+ x: 60,
2958
+ y: 49,
2959
+ width: 2,
2960
+ height: 12,
2961
+ rx: 1
2962
+ })))), (0, react.createElement)("rect", {
2963
+ width: "100%",
2964
+ height: "100%",
2965
+ fill: "url(#dsc-wave-pattern)"
2966
+ })), (0, react.createElement)("svg", {
2967
+ className: "dsc-asset-audio-icon-sym",
2968
+ viewBox: "0 0 24 24",
2969
+ "aria-hidden": "true",
2970
+ dangerouslySetInnerHTML: { __html: "<path fill=\"currentColor\" d=\"M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z\"/>" }
2971
+ }), (0, react.createElement)("span", {
2972
+ className: "dsc-asset-audio-icon-name",
2973
+ title: first.filename
2974
+ }, first.filename)) : (0, react.createElement)("img", {
2808
2975
  src: first.url,
2809
2976
  alt: first.filename,
2810
2977
  loading: "lazy",
@@ -3159,6 +3326,10 @@ select.dsc-input option { background: var(--dsw-alias-bg-layer-1); color: var(--
3159
3326
  .dsc-asset:hover .dsc-asset-trash, .dsc-asset-trash:focus-visible { opacity: 1; }
3160
3327
  .dsc-asset-trash:hover { background: var(--dsw-alias-state-error-primary); color: #fff; }
3161
3328
  .dsc-asset img, .dsc-asset video { width: 100%; height: 110px; object-fit: cover; display: block; background: #000; }
3329
+ .dsc-asset-audio-icon { position: relative; width: 100%; height: 110px; box-sizing: border-box; padding: 0 6px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; background: #26702b; overflow: hidden; }
3330
+ .dsc-asset-audio-icon-wave { position: absolute; inset: 0; width: 100%; height: 100%; }
3331
+ .dsc-asset-audio-icon-sym { position: relative; width: 42px; height: 42px; color: #fff; }
3332
+ .dsc-asset-audio-icon-name { position: relative; max-width: 100%; font-size: 11px; color: rgba(255, 255, 255, 0.9); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
3162
3333
  .dsc-asset--empty {
3163
3334
  height: 110px; padding: 8px; box-sizing: border-box;
3164
3335
  display: flex; flex-direction: column; justify-content: center; align-items: center; gap: 4px;