koishi-plugin-media-luna 0.0.4 → 0.0.6

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 (49) hide show
  1. package/client/api.ts +36 -86
  2. package/client/components/ChannelsView.vue +28 -208
  3. package/client/components/GenerateView.vue +306 -38
  4. package/client/components/HistoryGallery.vue +47 -12
  5. package/client/components/PresetsView.vue +26 -200
  6. package/client/components/SettingsView.vue +26 -0
  7. package/client/components/TasksView.vue +15 -68
  8. package/client/composables/index.ts +14 -0
  9. package/client/composables/useDataFetch.ts +102 -0
  10. package/client/composables/useDialog.ts +58 -0
  11. package/client/composables/useLoading.ts +84 -0
  12. package/client/composables/usePagination.ts +110 -0
  13. package/client/constants/categories.ts +36 -0
  14. package/client/constants/index.ts +5 -0
  15. package/client/constants/phases.ts +44 -0
  16. package/client/styles/shared.css +42 -0
  17. package/client/types.ts +73 -0
  18. package/dist/index.js +1 -1
  19. package/dist/style.css +1 -1
  20. package/lib/core/api/plugin-api.d.ts.map +1 -1
  21. package/lib/core/api/plugin-api.js +31 -0
  22. package/lib/core/api/plugin-api.js.map +1 -1
  23. package/lib/core/medialuna.service.d.ts.map +1 -1
  24. package/lib/core/medialuna.service.js +16 -12
  25. package/lib/core/medialuna.service.js.map +1 -1
  26. package/lib/core/pipeline/generation-pipeline.d.ts +4 -0
  27. package/lib/core/pipeline/generation-pipeline.d.ts.map +1 -1
  28. package/lib/core/pipeline/generation-pipeline.js +61 -20
  29. package/lib/core/pipeline/generation-pipeline.js.map +1 -1
  30. package/lib/core/plugin-loader.d.ts +42 -0
  31. package/lib/core/plugin-loader.d.ts.map +1 -1
  32. package/lib/core/plugin-loader.js +204 -1
  33. package/lib/core/plugin-loader.js.map +1 -1
  34. package/lib/core/request.service.d.ts +4 -1
  35. package/lib/core/request.service.d.ts.map +1 -1
  36. package/lib/core/request.service.js +11 -1
  37. package/lib/core/request.service.js.map +1 -1
  38. package/lib/core/types.d.ts +10 -0
  39. package/lib/core/types.d.ts.map +1 -1
  40. package/lib/plugins/README.md +716 -0
  41. package/lib/plugins/cache/middleware.d.ts.map +1 -1
  42. package/lib/plugins/cache/middleware.js +2 -0
  43. package/lib/plugins/cache/middleware.js.map +1 -1
  44. package/lib/plugins/task/middleware.d.ts.map +1 -1
  45. package/lib/plugins/task/middleware.js +20 -2
  46. package/lib/plugins/task/middleware.js.map +1 -1
  47. package/lib/types/index.d.ts +4 -0
  48. package/lib/types/index.d.ts.map +1 -1
  49. package/package.json +1 -1
package/client/api.ts CHANGED
@@ -11,9 +11,31 @@ import type {
11
11
  MiddlewareInfo,
12
12
  CardFieldsResponse,
13
13
  PaginatedResponse,
14
- ApiResponse
14
+ ApiResponse,
15
+ SettingsPanelInfo,
16
+ PluginInfo,
17
+ GalleryItem,
18
+ GalleryResponse,
19
+ RecentImage,
20
+ CacheFileInfo,
21
+ CacheStats,
22
+ ExternalPluginInfo,
23
+ CurrentUser
15
24
  } from './types'
16
25
 
26
+ // 重新导出类型,方便使用
27
+ export type {
28
+ GalleryItem,
29
+ GalleryResponse,
30
+ RecentImage,
31
+ CacheFileInfo,
32
+ CacheStats,
33
+ SettingsPanelInfo,
34
+ PluginInfo,
35
+ ExternalPluginInfo,
36
+ CurrentUser
37
+ }
38
+
17
39
  // 通用调用封装
18
40
  async function call<T>(event: keyof any, params?: any): Promise<T> {
19
41
  // @ts-ignore
@@ -65,28 +87,6 @@ export const taskApi = {
65
87
  call<PaginatedResponse<TaskData>>('media-luna/tasks/my', params)
66
88
  }
67
89
 
68
- // 画廊类型
69
- export interface GalleryItem {
70
- id: number
71
- prompt: string
72
- images: string[]
73
- createdAt: Date
74
- channelId: number
75
- }
76
-
77
- export interface GalleryResponse {
78
- items: GalleryItem[]
79
- total: number
80
- hasMore: boolean
81
- }
82
-
83
- export interface RecentImage {
84
- taskId: number
85
- url: string
86
- prompt: string
87
- createdAt: Date
88
- }
89
-
90
90
  // 画廊 API
91
91
  export const galleryApi = {
92
92
  /** 获取当前用户的生成画廊 */
@@ -133,25 +133,6 @@ export const generateApi = {
133
133
  call<{ channelName: string, connectorId: string, finalPrompt: string, parameters: any }>('media-luna/generate/preview', params)
134
134
  }
135
135
 
136
- // 缓存类型
137
- export interface CacheFileInfo {
138
- id: string
139
- url?: string // 可访问的 URL
140
- filename: string
141
- mime: string
142
- size: number
143
- createdAt?: Date
144
- accessedAt?: Date
145
- }
146
-
147
- export interface CacheStats {
148
- totalFiles: number
149
- totalSizeMB: number
150
- maxSizeMB: number
151
- oldestAccess: Date | null
152
- newestAccess: Date | null
153
- }
154
-
155
136
  // 缓存 API
156
137
  export const cacheApi = {
157
138
  /** 上传文件到缓存 */
@@ -177,20 +158,6 @@ export const cacheApi = {
177
158
  call<void>('media-luna/cache/clear')
178
159
  }
179
160
 
180
- // 设置面板类型
181
- export interface SettingsPanelInfo {
182
- id: string
183
- name: string
184
- icon: string
185
- description?: string
186
- order: number
187
- type: 'builtin' | 'custom'
188
- component?: string
189
- configFields?: ConfigField[]
190
- configKey?: string
191
- config?: Record<string, any>
192
- }
193
-
194
161
  // 设置面板 API
195
162
  export const settingsApi = {
196
163
  /** 获取所有设置面板 */
@@ -207,35 +174,6 @@ export const settingsApi = {
207
174
  call<{ added: number, updated: number, removed: number }>('media-luna/presets/sync')
208
175
  }
209
176
 
210
- // 插件信息类型
211
- export interface PluginInfo {
212
- id: string
213
- name: string
214
- description?: string
215
- version?: string
216
- enabled: boolean
217
- configFields: ConfigField[]
218
- config: Record<string, any>
219
- actions: Array<{
220
- name: string
221
- label: string
222
- type?: string
223
- icon?: string
224
- apiEvent: string
225
- }>
226
- middlewares: Array<{
227
- name: string
228
- displayName: string
229
- phase: string
230
- enabled: boolean
231
- }>
232
- connector?: {
233
- id: string
234
- name: string
235
- supportedTypes: string[]
236
- }
237
- }
238
-
239
177
  // 插件 API
240
178
  export const pluginApi = {
241
179
  /** 获取所有已加载插件 */
@@ -252,5 +190,17 @@ export const pluginApi = {
252
190
  call<void>('media-luna/plugins/enable', { id }),
253
191
  /** 禁用插件 */
254
192
  disable: (id: string) =>
255
- call<void>('media-luna/plugins/disable', { id })
193
+ call<void>('media-luna/plugins/disable', { id }),
194
+
195
+ // ============ 外部插件管理 ============
196
+
197
+ /** 获取已加载的外部插件列表 */
198
+ externalList: () =>
199
+ call<ExternalPluginInfo[]>('media-luna/plugins/external/list'),
200
+ /** 添加外部插件 */
201
+ externalAdd: (moduleName: string) =>
202
+ call<void>('media-luna/plugins/external/add', { moduleName }),
203
+ /** 移除外部插件 */
204
+ externalRemove: (moduleName: string) =>
205
+ call<void>('media-luna/plugins/external/remove', { moduleName })
256
206
  }
@@ -1,8 +1,8 @@
1
1
  <template>
2
- <div class="view-container">
3
- <div class="view-header">
4
- <div class="header-left">
5
- <k-button solid type="primary" @click="openCreateDialog" class="create-btn">
2
+ <div class="ml-view-container">
3
+ <div class="ml-view-header">
4
+ <div class="ml-header-left">
5
+ <k-button solid type="primary" @click="openCreateDialog">
6
6
  <template #icon><k-icon name="add"></k-icon></template>
7
7
  新建渠道
8
8
  </k-button>
@@ -12,18 +12,18 @@
12
12
  :preset-tags="presetTags"
13
13
  />
14
14
  </div>
15
- <div class="header-right">
15
+ <div class="ml-header-right">
16
16
  <ViewModeSwitch v-model="viewMode" />
17
17
  </div>
18
18
  </div>
19
19
 
20
- <div class="view-content">
20
+ <div class="ml-view-content">
21
21
  <LoadingState v-if="loading" />
22
22
 
23
23
  <!-- 卡片视图 -->
24
- <div v-else-if="viewMode === 'card'" class="channel-grid">
25
- <div v-for="channel in filteredChannels" :key="channel.id" class="channel-card-wrapper">
26
- <div class="channel-card" @click="openEditDialog(channel)">
24
+ <div v-else-if="viewMode === 'card'" class="ml-grid">
25
+ <div v-for="channel in filteredChannels" :key="channel.id">
26
+ <div class="ml-card ml-card--clickable" @click="openEditDialog(channel)">
27
27
  <div class="card-header">
28
28
  <div class="header-main">
29
29
  <div class="channel-name">
@@ -60,12 +60,12 @@
60
60
  </div>
61
61
 
62
62
  <div class="card-footer" @click.stop>
63
- <k-button size="mini" class="copy-btn" @click="copyChannel(channel)">
63
+ <k-button size="mini" class="ml-btn-outline-primary" @click="copyChannel(channel)">
64
64
  <template #icon><k-icon name="copy"></k-icon></template>
65
65
  复制
66
66
  </k-button>
67
67
  <div class="spacer"></div>
68
- <k-button size="mini" class="delete-btn" @click="confirmDelete(channel)">
68
+ <k-button size="mini" class="ml-btn-outline-danger" @click="confirmDelete(channel)">
69
69
  <template #icon><k-icon name="delete"></k-icon></template>
70
70
  删除
71
71
  </k-button>
@@ -75,8 +75,8 @@
75
75
  </div>
76
76
 
77
77
  <!-- 列表视图 -->
78
- <div v-else class="list-view">
79
- <table class="channel-table">
78
+ <div v-else class="ml-table-container">
79
+ <table class="ml-table">
80
80
  <thead>
81
81
  <tr>
82
82
  <th class="col-name">名称</th>
@@ -91,7 +91,6 @@
91
91
  <tr
92
92
  v-for="channel in filteredChannels"
93
93
  :key="channel.id"
94
- class="channel-row"
95
94
  @click="openEditDialog(channel)"
96
95
  >
97
96
  <td class="col-name">
@@ -118,11 +117,11 @@
118
117
  </td>
119
118
  <td class="col-actions" @click.stop>
120
119
  <div class="action-btns">
121
- <k-button size="mini" class="copy-btn" @click="copyChannel(channel)">
120
+ <k-button size="mini" class="ml-btn-outline-primary" @click="copyChannel(channel)">
122
121
  <template #icon><k-icon name="copy"></k-icon></template>
123
122
  复制
124
123
  </k-button>
125
- <k-button size="mini" class="delete-btn" @click="confirmDelete(channel)">
124
+ <k-button size="mini" class="ml-btn-outline-danger" @click="confirmDelete(channel)">
126
125
  <template #icon><k-icon name="delete"></k-icon></template>
127
126
  删除
128
127
  </k-button>
@@ -348,62 +347,11 @@ onMounted(() => {
348
347
  </script>
349
348
 
350
349
  <style scoped>
351
- .view-container {
352
- height: 100%;
353
- display: flex;
354
- flex-direction: column;
355
- min-height: 0;
356
- }
350
+ @import '../styles/shared.css';
357
351
 
358
- .view-header {
359
- flex-shrink: 0;
360
- display: flex;
361
- justify-content: space-between;
362
- align-items: center;
363
- margin-bottom: 1rem;
364
- }
352
+ /* ========== 渠道卡片特有样式 ========== */
365
353
 
366
- .header-left {
367
- display: flex;
368
- align-items: center;
369
- gap: 1.5rem;
370
- flex-wrap: wrap;
371
- }
372
-
373
- .view-content {
374
- flex: 1 1 0;
375
- min-height: 0;
376
- overflow-y: auto;
377
- overflow-x: hidden;
378
- padding: 0.5rem 0;
379
- }
380
-
381
- .channel-grid {
382
- display: grid;
383
- grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
384
- gap: 1.5rem;
385
- padding: 0.5rem;
386
- }
387
-
388
- .channel-card {
389
- height: 100%;
390
- display: flex;
391
- flex-direction: column;
392
- transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
393
- border: 1px solid var(--k-color-border);
394
- border-radius: 8px; /* Slightly tighter radius */
395
- background-color: var(--k-card-bg);
396
- position: relative;
397
- overflow: hidden;
398
- }
399
-
400
- .channel-card:hover {
401
- border-color: var(--k-color-active);
402
- box-shadow: 0 8px 16px -4px rgba(0, 0, 0, 0.05); /* Modern subtle shadow */
403
- transform: translateY(-1px);
404
- }
405
-
406
- /* Seamless Header Concept */
354
+ /* 卡片内部布局 */
407
355
  .card-header {
408
356
  padding: 1rem 1.25rem 0.75rem;
409
357
  display: flex;
@@ -433,12 +381,6 @@ onMounted(() => {
433
381
  gap: 0.5rem;
434
382
  }
435
383
 
436
- .channel-id {
437
- font-size: 0.8rem;
438
- color: var(--k-color-text-description);
439
- font-weight: normal;
440
- }
441
-
442
384
  .connector-badge {
443
385
  display: inline-flex;
444
386
  align-items: center;
@@ -507,18 +449,11 @@ onMounted(() => {
507
449
  font-size: 0.75rem;
508
450
  padding: 1px 6px;
509
451
  color: var(--k-color-text-description);
510
- border: 1px solid var(--k-color-border); /* Outline style */
452
+ border: 1px solid var(--k-color-border);
511
453
  border-radius: 12px;
512
454
  background-color: transparent;
513
455
  }
514
456
 
515
- .no-tags {
516
- color: var(--k-color-text-description);
517
- font-size: 0.85rem;
518
- font-style: italic;
519
- opacity: 0.7;
520
- }
521
-
522
457
  .card-footer {
523
458
  padding: 0.75rem 1.25rem;
524
459
  border-top: 1px solid var(--k-color-border);
@@ -532,85 +467,17 @@ onMounted(() => {
532
467
  flex-grow: 1;
533
468
  }
534
469
 
535
- /* List View - Table Style */
536
- .list-view {
537
- background-color: var(--k-card-bg);
538
- border: 1px solid var(--k-color-border);
539
- border-radius: 8px;
540
- overflow: hidden;
541
- }
542
-
543
- .channel-table {
544
- width: 100%;
545
- border-collapse: collapse;
546
- table-layout: fixed;
547
- }
548
-
549
- .channel-table thead {
550
- background-color: var(--k-color-bg-2);
551
- }
552
-
553
- .channel-table th {
554
- padding: 0.875rem 1rem;
555
- font-size: 0.8rem;
556
- font-weight: 600;
557
- color: var(--k-color-text-description);
558
- text-align: left;
559
- border-bottom: 1px solid var(--k-color-border);
560
- white-space: nowrap;
561
- overflow: hidden;
562
- text-overflow: ellipsis;
563
- }
564
-
565
- .channel-table .channel-row {
566
- cursor: pointer;
567
- transition: background-color 0.15s ease;
568
- }
569
-
570
- .channel-table .channel-row:hover {
571
- background-color: var(--k-color-bg-1);
572
- }
573
-
574
- .channel-table .channel-row td {
575
- padding: 0.75rem 1rem;
576
- border-bottom: 1px solid var(--k-color-border);
577
- vertical-align: middle;
578
- text-align: left;
579
- overflow: hidden;
580
- text-overflow: ellipsis;
581
- white-space: nowrap;
582
- }
583
-
584
- .channel-table .channel-row:last-child td {
585
- border-bottom: none;
586
- }
470
+ /* ========== 列表视图特有样式 ========== */
587
471
 
588
472
  /* 表格列宽定义 */
589
- .channel-table .col-name {
590
- width: 20%;
591
- }
592
-
593
- .channel-table .col-connector {
594
- width: 15%;
595
- }
596
-
597
- .channel-table .col-tags {
598
- width: auto;
599
- }
600
-
601
- .channel-table .col-cost {
602
- width: 12%;
603
- }
604
-
605
- .channel-table .col-status {
606
- width: 8%;
607
- }
608
-
609
- .channel-table .col-actions {
610
- width: 15%;
611
- }
612
-
613
- .channel-table .name-text {
473
+ .col-name { width: 20%; }
474
+ .col-connector { width: 15%; }
475
+ .col-tags { width: auto; }
476
+ .col-cost { width: 12%; }
477
+ .col-status { width: 8%; }
478
+ .col-actions { width: 15%; }
479
+
480
+ .name-text {
614
481
  font-weight: 600;
615
482
  color: var(--k-color-text);
616
483
  display: block;
@@ -619,17 +486,6 @@ onMounted(() => {
619
486
  white-space: nowrap;
620
487
  }
621
488
 
622
- .channel-table .connector-badge {
623
- display: inline-flex;
624
- align-items: center;
625
- gap: 0.35rem;
626
- padding: 2px 8px;
627
- background-color: var(--k-color-bg-2);
628
- border-radius: 4px;
629
- font-size: 0.8rem;
630
- color: var(--k-color-text-description);
631
- }
632
-
633
489
  .tags-wrapper {
634
490
  display: flex;
635
491
  flex-wrap: wrap;
@@ -659,40 +515,4 @@ onMounted(() => {
659
515
  display: flex;
660
516
  gap: 0.5rem;
661
517
  }
662
-
663
- .view-header {
664
- display: flex;
665
- justify-content: space-between;
666
- align-items: center;
667
- margin-bottom: 1.5rem;
668
- padding: 0 0.5rem;
669
- }
670
-
671
- .header-right {
672
- display: flex;
673
- align-items: center;
674
- gap: 1rem;
675
- }
676
-
677
- /* 复制按钮样式 */
678
- .copy-btn {
679
- color: var(--k-color-active) !important;
680
- border-color: var(--k-color-active) !important;
681
- }
682
-
683
- .copy-btn:hover {
684
- background-color: var(--k-color-active) !important;
685
- color: white !important;
686
- }
687
-
688
- /* 删除按钮样式 */
689
- .delete-btn {
690
- color: var(--k-color-error, #f56c6c) !important;
691
- border-color: var(--k-color-error, #f56c6c) !important;
692
- }
693
-
694
- .delete-btn:hover {
695
- background-color: var(--k-color-error, #f56c6c) !important;
696
- color: white !important;
697
- }
698
518
  </style>