dsh-audiogen 0.4.25 → 0.4.26

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-audiogen",
3
3
  "description": "AI audio generation plugin for the dsh web GUI: multi-vendor TTS/music/sound-effect channels (OpenAI-compatible, ElevenLabs, MiniMax, Stability AI and custom), per-channel model/voice catalogs, Agent tool and a sidebar AI 音频 panel.",
4
- "version": "0.4.25",
4
+ "version": "0.4.26",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "exports": {
@@ -12,10 +12,9 @@
12
12
  import { useEffect, useMemo, useState } from 'react'
13
13
  import type { AudiogenApi } from './api.ts'
14
14
  import type { AudiogenScope } from './settings-scope.ts'
15
- import type { VoiceEntry, VoiceRecommendRecord } from '../protocol.ts'
16
- import { tt } from './helpers.ts'
15
+ import type { VoiceEntry } from '../protocol.ts'
17
16
  import { AudioPlayer } from './audio-player.tsx'
18
- import { MicIcon, SearchIcon, TrashIcon, CheckIcon, ListIcon } from './icons.tsx'
17
+ import { MicIcon, SearchIcon, TrashIcon, CheckIcon } from './icons.tsx'
19
18
  import css from './voices.module.css'
20
19
 
21
20
  export interface VoicesReusePayload {
@@ -100,26 +99,6 @@ export function VoicesView(props: {
100
99
  const [note, setNote] = useState<string | null>(null)
101
100
  const [deletingId, setDeletingId] = useState<string | null>(null)
102
101
 
103
- // AI 推荐记录(每次推荐自动落盘,最多保留 50 条;关闭面板后仍可回看)
104
- const [records, setRecords] = useState<VoiceRecommendRecord[]>([])
105
- const [recordsOpen, setRecordsOpen] = useState(false)
106
-
107
- const loadRecords = async (): Promise<void> => {
108
- try {
109
- const response = await api.voiceRecommendHistory(30)
110
- setRecords(response.entries ?? [])
111
- } catch {
112
- // best-effort:记录加载失败不打扰主流程
113
- }
114
- }
115
-
116
- const removeRecord = async (id: string): Promise<void> => {
117
- await api.voiceRecommendHistoryRemove(id).catch(() => { /* best-effort */ })
118
- void loadRecords()
119
- }
120
-
121
- useEffect(() => { void loadRecords() }, [api])
122
-
123
102
  /** 当前的筛选载荷(查询音色列表用)。 */
124
103
  const currentFilterPayload = (): Record<string, unknown> => ({
125
104
  channel: channelId,
@@ -201,20 +180,6 @@ export function VoicesView(props: {
201
180
  })
202
181
  }
203
182
 
204
- /** 从推荐记录里复用音色:切到记录对应的渠道,再回填 TTS 表单。 */
205
- const reuseFromRecord = (record: VoiceRecommendRecord, voice: { voice_id: string }): void => {
206
- if (record.channel_id !== undefined && record.channel_id !== '' && channels.some(item => item.id === record.channel_id)) {
207
- setChannelId(record.channel_id)
208
- }
209
- const channel = channels.find(item => item.id === (record.channel_id ?? channelId))
210
- const ttsModel = channel?.models.find(model => model.category === 'tts' || /tts|speech|voice|t2a/i.test(model.alias))
211
- onReuseVoice({
212
- mode: 'tts',
213
- voiceId: voice.voice_id,
214
- ...(ttsModel === undefined || ttsModel.alias === '' ? {} : { model: ttsModel.alias }),
215
- })
216
- }
217
-
218
183
  return (
219
184
  <div className={css.view}>
220
185
  <div className={css.toolbar}>
@@ -254,56 +219,9 @@ export function VoicesView(props: {
254
219
  </div>
255
220
 
256
221
  <p className={css.stateNote}>
257
- 按需求描述(prompt)推荐音色请使用生成页左侧「🎤 音色推荐」模式;这里仅浏览/筛选/删除音色,下方为历次 AI 推荐记录。
222
+ 按需求描述(prompt)推荐音色请使用生成页左侧「🎤 音色推荐」模式;这里仅浏览/筛选/删除音色。
258
223
  </p>
259
224
 
260
- {records.length > 0 ? (
261
- <details
262
- className={css.recordBox}
263
- open={recordsOpen}
264
- onToggle={(event) => setRecordsOpen((event.target as HTMLDetailsElement).open)}
265
- >
266
- <summary className={css.recordSummary}>
267
- <ListIcon /> 最近 AI 推荐({records.length})
268
- <span className={css.recordHint}>每次推荐自动记录,点「用此音色生成」可直接回填 TTS 表单</span>
269
- </summary>
270
- <div className={css.recordList}>
271
- {records.map(record => (
272
- <div className={`${css.card} ${css.recordCard}`} key={record.id}>
273
- <div className={css.cardHead}>
274
- <span className={css.voiceName} title={record.requirement}>{record.requirement}</span>
275
- <span className={css.badge} data-source={record.vendor}>{record.channel}</span>
276
- </div>
277
- <div className={css.meta}>
278
- <span>{new Date(record.createdAt).toLocaleString()}</span>
279
- <span>候选池 {record.candidate_count}</span>
280
- <span>推荐 {record.recommendations.length} 条</span>
281
- </div>
282
- <div className={css.recordVoices}>
283
- {record.recommendations.map((voice, index) => (
284
- <div className={css.recordVoiceRow} key={`${record.id}:${voice.voice_id}`}>
285
- <span className={css.rank}>{index + 1}</span>
286
- <span className={css.recordVoiceName} title={voice.name}>{voice.name}</span>
287
- <span className={css.voiceId} title={voice.voice_id}>ID: {voice.voice_id}</span>
288
- {voice.descriptive === undefined ? null : <span className={css.reasonInline}>descriptive: {voice.descriptive}</span>}
289
- {voice.reason === '' ? null : <span className={css.reasonInline}>{voice.reason}</span>}
290
- <button type="button" className={css.useBtn} onClick={() => reuseFromRecord(record, voice)}>
291
- <MicIcon /> 用此音色生成
292
- </button>
293
- </div>
294
- ))}
295
- </div>
296
- <div className={css.actions}>
297
- <button type="button" className={css.deleteBtn} onClick={() => void removeRecord(record.id)}>
298
- <TrashIcon /> 删除记录
299
- </button>
300
- </div>
301
- </div>
302
- ))}
303
- </div>
304
- </details>
305
- ) : null}
306
-
307
225
  <details className={css.advanced}>
308
226
  <summary className={css.advancedSummary}>官方共享库筛选(ElevenLabs /v1/shared-voices,其它渠道无效)</summary>
309
227
  <div className={css.advancedGrid}>
@@ -323,94 +323,9 @@
323
323
  background: color-mix(in srgb, var(--dsw-accent, #2563eb) 4%, var(--dsw-alias-bg, #fff));
324
324
  }
325
325
 
326
- .rank {
327
- display: inline-flex;
328
- align-items: center;
329
- justify-content: center;
330
- width: 20px;
331
- height: 20px;
332
- border-radius: 999px;
333
- background: var(--dsw-accent, #2563eb);
334
- color: #fff;
335
- font-size: 11px;
336
- font-weight: 600;
337
- flex: none;
338
- }
339
-
340
326
  .reason {
341
327
  margin: 0;
342
328
  font-size: 12.5px;
343
329
  line-height: 1.5;
344
330
  color: var(--dsw-accent, #2563eb);
345
331
  }
346
-
347
- /* ------------------------------------------------------ AI 推荐记录 */
348
- .recordBox {
349
- border: 1px solid var(--dsw-alias-border-l2, #e5e7eb);
350
- border-radius: 10px;
351
- background: var(--dsw-alias-bg-layer-2, #f9fafb);
352
- padding: 8px 12px;
353
- }
354
-
355
- .recordSummary {
356
- display: flex;
357
- align-items: center;
358
- gap: 6px;
359
- cursor: pointer;
360
- font-size: 13px;
361
- font-weight: 600;
362
- color: var(--dsw-alias-fg, #111827);
363
- user-select: none;
364
- }
365
-
366
- .recordHint {
367
- font-size: 11.5px;
368
- font-weight: 400;
369
- color: var(--dsw-alias-fg-l3, #6b7280);
370
- }
371
-
372
- .recordList {
373
- display: flex;
374
- flex-direction: column;
375
- gap: 8px;
376
- margin-top: 8px;
377
- }
378
-
379
- .recordCard {
380
- border-left: 3px solid var(--dsw-accent, #2563eb);
381
- }
382
-
383
- .recordVoices {
384
- display: flex;
385
- flex-direction: column;
386
- gap: 4px;
387
- margin-top: 6px;
388
- }
389
-
390
- .recordVoiceRow {
391
- display: flex;
392
- align-items: center;
393
- gap: 8px;
394
- flex-wrap: wrap;
395
- padding: 4px 2px;
396
- border-radius: 6px;
397
- }
398
-
399
- .recordVoiceRow:hover {
400
- background: color-mix(in srgb, var(--dsw-accent, #2563eb) 5%, transparent);
401
- }
402
-
403
- .recordVoiceName {
404
- font-size: 13px;
405
- font-weight: 500;
406
- color: var(--dsw-alias-fg, #111827);
407
- overflow: hidden;
408
- text-overflow: ellipsis;
409
- white-space: nowrap;
410
- max-width: 220px;
411
- }
412
-
413
- .reasonInline {
414
- font-size: 12px;
415
- color: var(--dsw-accent, #2563eb);
416
- }