@dsh-plus/image-studio 0.1.0 → 0.1.2

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.
@@ -1,33 +1,587 @@
1
1
  /**
2
- * 画廊/生图占位样式:本次交付仅注入极简占位类名(无视觉设计)。
3
- * 沿用官方约定 <style data-plugin data-plugin-css>,HMR 按此卸载。
2
+ * 图像工作室前端样式:侧栏入口 + 工作台(官方 Modal 承载)+ 配置卡片附加件。
3
+ * 设计令牌仅用 dsh-client-ui-theme 实测存在的 --dsw-alias-*(深浅色自适应);
4
+ * 模态骨架(遮罩/Escape/portal 层叠)由官方 Modal 原语承载,本表只覆写尺寸
5
+ * 与内部布局(.ims-modal 等,模式同 web-files/web-terminal 的 wf-/wt-modal)。
6
+ * 响应式断点 767px 与 @dsh-plus/ui-mobile-fit 对齐(移动端全屏、单列、44px 热区)。
4
7
  * @module image-studio/client/styles
5
8
  */
6
9
 
7
10
  const PKG = '@dsh-plus/image-studio'
11
+ const STYLE_TAG_ID = `${PKG}/styles.css`
8
12
 
9
- /** 注入设置页占位样式。 */
10
- export function injectSectionStyle(): HTMLStyleElement | null {
11
- return inject(
12
- 'dsh-plus-image-studio-section',
13
- '.dsh-plus-image-studio-section-placeholder{padding:16px;opacity:.6}',
14
- )
13
+ export const imageStudioCss = `
14
+ /* ── 侧边栏入口(与文件/终端入口同款;flex:1 1 0 与邻居等分 footer 行宽)── */
15
+ .ims-entry {
16
+ box-sizing: border-box;
17
+ display: flex;
18
+ flex: 1 1 0;
19
+ min-width: 0;
20
+ align-items: center;
21
+ gap: 8px;
22
+ width: 100%;
23
+ height: 42px;
24
+ margin: 4px 0;
25
+ padding: 0 10px 0 8px;
26
+ border: none;
27
+ border-radius: 12px;
28
+ background: transparent;
29
+ color: var(--dsw-alias-label-primary);
30
+ font-family: inherit;
31
+ font-size: 14px;
32
+ line-height: 22px;
33
+ cursor: pointer;
34
+ overflow: hidden;
15
35
  }
36
+ .ims-entry:hover { background: var(--dsw-alias-interactive-bg-hover); }
37
+ .ims-entry-rail {
38
+ flex: none;
39
+ width: 36px;
40
+ height: 36px;
41
+ margin: 8px 0 10px;
42
+ padding: 0;
43
+ border-radius: 50%;
44
+ justify-content: center;
45
+ gap: 0;
46
+ }
47
+ .ims-entry-label { white-space: nowrap; overflow: hidden; }
48
+
49
+ /* ── 工作台模态尺寸覆写(官方 Modal headless 卡片)── */
50
+ .ims-modal {
51
+ width: min(1100px, 94vw) !important;
52
+ max-width: none !important;
53
+ height: min(780px, 88vh);
54
+ padding: 0 !important;
55
+ gap: 0 !important;
56
+ display: flex;
57
+ flex-direction: column;
58
+ overflow: hidden;
59
+ }
60
+
61
+ /* ── 面板骨架 ── */
62
+ .ims-panel {
63
+ display: flex;
64
+ flex-direction: column;
65
+ height: 100%;
66
+ min-height: 0;
67
+ position: relative;
68
+ color: var(--dsw-alias-label-primary);
69
+ }
70
+ .ims-head {
71
+ display: flex;
72
+ flex: none;
73
+ align-items: center;
74
+ gap: 8px;
75
+ padding: 10px 12px;
76
+ border-bottom: 1px solid var(--dsw-alias-border-l3);
77
+ }
78
+ .ims-tabs { display: flex; flex: 1; gap: 4px; min-width: 0; }
79
+ .ims-tab {
80
+ display: inline-flex;
81
+ align-items: center;
82
+ gap: 6px;
83
+ height: 34px;
84
+ padding: 0 14px;
85
+ border: none;
86
+ border-radius: 10px;
87
+ background: transparent;
88
+ color: var(--dsw-alias-label-secondary);
89
+ font-family: inherit;
90
+ font-size: 14px;
91
+ cursor: pointer;
92
+ white-space: nowrap;
93
+ }
94
+ .ims-tab:hover { background: var(--dsw-alias-interactive-bg-hover); }
95
+ .ims-tabActive {
96
+ background: var(--dsw-alias-button-elevated-fill);
97
+ color: var(--dsw-alias-label-primary);
98
+ font-weight: 500;
99
+ }
100
+ .ims-iconBtn {
101
+ display: inline-flex;
102
+ flex: none;
103
+ align-items: center;
104
+ justify-content: center;
105
+ width: 32px;
106
+ height: 32px;
107
+ border: none;
108
+ border-radius: 8px;
109
+ background: transparent;
110
+ color: var(--dsw-alias-label-primary);
111
+ cursor: pointer;
112
+ }
113
+ .ims-iconBtn:hover { background: var(--dsw-alias-interactive-bg-hover); }
114
+ .ims-body { flex: 1; min-height: 0; overflow-y: auto; }
115
+
116
+ /* ── 通用件 ── */
117
+ .ims-input {
118
+ box-sizing: border-box;
119
+ width: 100%;
120
+ min-height: 34px;
121
+ padding: 6px 10px;
122
+ border: 1px solid var(--dsw-alias-border-l2);
123
+ border-radius: 8px;
124
+ background: var(--dsw-alias-bg-layer-3);
125
+ color: var(--dsw-alias-label-primary);
126
+ font-family: inherit;
127
+ font-size: 13px;
128
+ line-height: 20px;
129
+ }
130
+ .ims-input:focus { outline: 2px solid var(--dsw-alias-brand-primary); outline-offset: 0; }
131
+ .ims-btn {
132
+ display: inline-flex;
133
+ align-items: center;
134
+ justify-content: center;
135
+ gap: 6px;
136
+ min-height: 34px;
137
+ padding: 6px 14px;
138
+ border: none;
139
+ border-radius: 8px;
140
+ font-family: inherit;
141
+ font-size: 13px;
142
+ font-weight: 500;
143
+ cursor: pointer;
144
+ white-space: nowrap;
145
+ }
146
+ .ims-btn:disabled { opacity: .5; cursor: not-allowed; }
147
+ .ims-btnPrimary {
148
+ background: var(--dsw-alias-button-primary-fill);
149
+ color: var(--dsw-alias-label-primary-inverted);
150
+ }
151
+ .ims-btnPrimary:hover:not(:disabled) { background: var(--dsw-alias-button-primary-hover); }
152
+ .ims-btnGhost {
153
+ background: transparent;
154
+ color: var(--dsw-alias-label-primary);
155
+ border: 1px solid var(--dsw-alias-border-l2);
156
+ }
157
+ .ims-btnGhost:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); }
158
+ .ims-btnDanger { background: var(--dsw-alias-state-error-primary); color: var(--dsw-alias-label-primary-inverted); }
159
+ .ims-btnSmall { min-height: 28px; padding: 3px 10px; font-size: 12px; }
160
+ .ims-hint { margin: 4px 0 0; font-size: 12px; color: var(--dsw-alias-label-tertiary); }
161
+ .ims-empty {
162
+ padding: 32px 16px;
163
+ text-align: center;
164
+ font-size: 13px;
165
+ color: var(--dsw-alias-label-tertiary);
166
+ }
167
+ .ims-badge {
168
+ flex: none;
169
+ padding: 2px 8px;
170
+ border-radius: 999px;
171
+ font-size: 11px;
172
+ line-height: 16px;
173
+ }
174
+ .ims-badgeOn { background: var(--dsw-alias-state-success-secondary); color: var(--dsw-alias-state-success-primary); }
175
+ .ims-badgeOff { background: var(--dsw-alias-state-error-secondary); color: var(--dsw-alias-state-error-primary); }
176
+ .ims-status { margin: 8px 0 0; font-size: 12px; color: var(--dsw-alias-state-success-primary); }
177
+ .ims-statusError { color: var(--dsw-alias-state-error-primary); }
178
+ .ims-spin { animation: ims-rotate 1s linear infinite; }
179
+ @keyframes ims-rotate { to { transform: rotate(360deg); } }
180
+
181
+ /* ── 生图表单 ── */
182
+ .ims-gen {
183
+ display: grid;
184
+ grid-template-columns: minmax(0, 5fr) minmax(0, 4fr);
185
+ gap: 0 20px;
186
+ padding: 16px;
187
+ }
188
+ .ims-gen[hidden] { display: none; }
189
+ .ims-genMain, .ims-genSide { min-width: 0; }
190
+ .ims-block { margin-bottom: 16px; }
191
+ .ims-blockTitle { margin: 0 0 8px; font-size: 13px; font-weight: 600; }
192
+ .ims-blockHead { align-items: center; }
193
+ .ims-blockHead .ims-blockTitle { margin: 0; flex: none; }
194
+ .ims-providerRow { display: flex; align-items: center; gap: 8px; }
195
+ .ims-checkLine {
196
+ display: flex;
197
+ align-items: center;
198
+ gap: 8px;
199
+ margin-top: 10px;
200
+ font-size: 13px;
201
+ cursor: pointer;
202
+ }
203
+ .ims-inlineGrid { display: grid; gap: 8px; margin-top: 8px; }
204
+ .ims-mini { display: grid; gap: 4px; font-size: 12px; color: var(--dsw-alias-label-secondary); }
205
+ .ims-prompt { resize: vertical; min-height: 96px; }
206
+ .ims-rowActions { display: flex; gap: 8px; margin-top: 8px; align-items: center; }
207
+ .ims-rowGrow { flex: 1; min-width: 0; }
208
+
209
+ /* 参数表单 */
210
+ .ims-paramGroup { display: grid; gap: 6px; }
211
+ .ims-paramRow {
212
+ display: flex;
213
+ align-items: center;
214
+ gap: 10px;
215
+ padding: 6px 8px;
216
+ border-radius: 8px;
217
+ }
218
+ .ims-paramRowOn { background: var(--dsw-alias-bg-layer-1); }
219
+ .ims-paramText { flex: 1; min-width: 0; display: grid; }
220
+ .ims-paramKey { font-size: 12px; font-weight: 600; font-family: var(--ds-font-family-code); }
221
+ .ims-paramGen {
222
+ margin-left: 6px;
223
+ padding: 0 5px;
224
+ border-radius: 4px;
225
+ background: var(--dsw-alias-bg-module-platform);
226
+ color: var(--dsw-alias-label-secondary);
227
+ font-size: 10px;
228
+ font-weight: 400;
229
+ }
230
+ .ims-paramDesc {
231
+ font-size: 11px;
232
+ color: var(--dsw-alias-label-tertiary);
233
+ overflow: hidden;
234
+ text-overflow: ellipsis;
235
+ white-space: nowrap;
236
+ }
237
+ .ims-paramControl { width: 132px; flex: none; min-height: 30px; padding: 4px 8px; }
238
+ .ims-adv { margin-top: 8px; }
239
+ .ims-advSummary {
240
+ cursor: pointer;
241
+ font-size: 12px;
242
+ font-weight: 600;
243
+ padding: 6px 0;
244
+ color: var(--dsw-alias-label-secondary);
245
+ user-select: none;
246
+ }
247
+ .ims-advCount {
248
+ margin-left: 6px;
249
+ padding: 0 6px;
250
+ border-radius: 999px;
251
+ background: var(--dsw-alias-button-primary-fill);
252
+ color: var(--dsw-alias-label-primary-inverted);
253
+ font-size: 10px;
254
+ }
255
+
256
+ /* 开关(switch) */
257
+ .ims-switch { position: relative; display: inline-flex; flex: none; width: 30px; height: 18px; }
258
+ .ims-switch input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
259
+ .ims-switchTrack {
260
+ position: absolute;
261
+ inset: 0;
262
+ border-radius: 999px;
263
+ background: var(--dsw-alias-bg-module-platform);
264
+ transition: background .15s;
265
+ pointer-events: none;
266
+ }
267
+ .ims-switchTrack::after {
268
+ content: '';
269
+ position: absolute;
270
+ top: 2px;
271
+ left: 2px;
272
+ width: 14px;
273
+ height: 14px;
274
+ border-radius: 50%;
275
+ background: var(--dsw-alias-label-primary);
276
+ transition: transform .15s;
277
+ }
278
+ .ims-switch input:checked + .ims-switchTrack { background: var(--dsw-alias-brand-primary); }
279
+ .ims-switch input:checked + .ims-switchTrack::after { transform: translateX(12px); }
280
+
281
+ /* 源图/遮罩缩略 */
282
+ .ims-sources { display: flex; flex-wrap: wrap; gap: 8px; }
283
+ .ims-thumb { position: relative; width: 64px; height: 64px; border-radius: 8px; overflow: hidden; }
284
+ .ims-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
285
+ .ims-thumbRemove {
286
+ position: absolute;
287
+ top: 2px;
288
+ right: 2px;
289
+ width: 18px;
290
+ height: 18px;
291
+ border: none;
292
+ border-radius: 50%;
293
+ background: rgba(0,0,0,.55);
294
+ color: #fff;
295
+ font-size: 12px;
296
+ line-height: 1;
297
+ cursor: pointer;
298
+ }
299
+ .ims-thumbAdd {
300
+ display: inline-flex;
301
+ flex-direction: column;
302
+ align-items: center;
303
+ justify-content: center;
304
+ gap: 4px;
305
+ width: 64px;
306
+ height: 64px;
307
+ border: 1px dashed var(--dsw-alias-border-l1);
308
+ border-radius: 8px;
309
+ background: transparent;
310
+ color: var(--dsw-alias-label-secondary);
311
+ font-size: 11px;
312
+ cursor: pointer;
313
+ }
314
+ .ims-thumbAdd:hover { background: var(--dsw-alias-interactive-bg-hover); }
315
+
316
+ /* 提交区 */
317
+ .ims-genFoot { grid-column: 1 / -1; border-top: 1px solid var(--dsw-alias-border-l3); padding-top: 12px; }
318
+ .ims-naming { display: flex; gap: 8px; margin-bottom: 8px; }
319
+ .ims-submit { width: 100%; min-height: 40px; font-size: 14px; margin-top: 8px; }
320
+
321
+ /* ── 子模态(图片选择器 / 画廊详情,官方 Modal 承载)── */
322
+ .ims-pickerModal {
323
+ width: min(760px, 92vw) !important;
324
+ max-width: none !important;
325
+ max-height: 86vh;
326
+ padding: 0 !important;
327
+ gap: 0 !important;
328
+ }
329
+ .ims-detailModal {
330
+ width: min(920px, 94vw) !important;
331
+ max-width: none !important;
332
+ max-height: 88vh;
333
+ padding: 0 !important;
334
+ gap: 0 !important;
335
+ }
336
+ .ims-sub {
337
+ display: flex;
338
+ flex-direction: column;
339
+ min-height: 0;
340
+ max-height: inherit;
341
+ color: var(--dsw-alias-label-primary);
342
+ }
343
+ .ims-subHead {
344
+ display: flex;
345
+ flex: none;
346
+ align-items: center;
347
+ justify-content: space-between;
348
+ gap: 8px;
349
+ padding: 12px 14px;
350
+ border-bottom: 1px solid var(--dsw-alias-border-l3);
351
+ }
352
+ .ims-subTitle { margin: 0; font-size: 14px; }
353
+ .ims-subFoot {
354
+ display: flex;
355
+ flex: none;
356
+ align-items: center;
357
+ justify-content: flex-end;
358
+ gap: 8px;
359
+ padding: 10px 14px;
360
+ border-top: 1px solid var(--dsw-alias-border-l3);
361
+ }
362
+ .ims-subFoot .ims-status { margin: 0 auto 0 0; }
363
+
364
+ /* 选择器网格 */
365
+ .ims-pickerGrid {
366
+ flex: 1;
367
+ min-height: 0;
368
+ overflow-y: auto;
369
+ display: grid;
370
+ grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
371
+ gap: 8px;
372
+ padding: 12px 14px;
373
+ }
374
+ .ims-pickerCell {
375
+ position: relative;
376
+ aspect-ratio: 1;
377
+ border: 2px solid transparent;
378
+ border-radius: 8px;
379
+ padding: 0;
380
+ background: none;
381
+ cursor: pointer;
382
+ overflow: hidden;
383
+ }
384
+ .ims-pickerCell img { width: 100%; height: 100%; object-fit: cover; display: block; }
385
+ .ims-pickerCellOn { border-color: var(--dsw-alias-brand-primary); }
386
+ .ims-pickerMark {
387
+ position: absolute;
388
+ top: 4px;
389
+ right: 4px;
390
+ min-width: 18px;
391
+ height: 18px;
392
+ border-radius: 999px;
393
+ background: var(--dsw-alias-button-primary-fill);
394
+ color: var(--dsw-alias-label-primary-inverted);
395
+ font-size: 11px;
396
+ line-height: 18px;
397
+ text-align: center;
398
+ }
399
+
400
+ /* ── 画廊 ── */
401
+ .ims-gallery { padding: 12px 16px; }
402
+ .ims-gallery[hidden] { display: none; }
403
+ .ims-galleryBar { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
404
+ .ims-grid {
405
+ display: grid;
406
+ grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
407
+ gap: 10px;
408
+ }
409
+ .ims-cell {
410
+ position: relative;
411
+ aspect-ratio: 1;
412
+ border: none;
413
+ border-radius: 10px;
414
+ padding: 0;
415
+ background: var(--dsw-alias-bg-layer-1);
416
+ cursor: pointer;
417
+ overflow: hidden;
418
+ }
419
+ .ims-cell img { width: 100%; height: 100%; object-fit: cover; display: block; }
420
+ .ims-cellPrompt {
421
+ position: absolute;
422
+ left: 0;
423
+ right: 0;
424
+ bottom: 0;
425
+ padding: 14px 8px 6px;
426
+ background: linear-gradient(transparent, rgba(0,0,0,.72));
427
+ color: #fff;
428
+ font-size: 11px;
429
+ text-align: left;
430
+ overflow: hidden;
431
+ text-overflow: ellipsis;
432
+ white-space: nowrap;
433
+ opacity: 0;
434
+ transition: opacity .15s;
435
+ }
436
+ .ims-cell:hover .ims-cellPrompt, .ims-cell:focus-visible .ims-cellPrompt { opacity: 1; }
437
+
438
+ /* 详情 */
439
+ .ims-detailMeta { font-size: 12px; color: var(--dsw-alias-label-tertiary); }
440
+ .ims-detailBody {
441
+ flex: 1;
442
+ min-height: 0;
443
+ overflow-y: auto;
444
+ display: grid;
445
+ grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
446
+ gap: 16px;
447
+ padding: 14px;
448
+ }
449
+ .ims-detailImages { display: grid; gap: 10px; align-content: start; }
450
+ .ims-detailFigure { margin: 0; display: grid; gap: 6px; }
451
+ .ims-detailFigure img {
452
+ width: 100%;
453
+ border-radius: 10px;
454
+ background: var(--dsw-alias-bg-layer-1);
455
+ display: block;
456
+ }
457
+ .ims-detailFigure figcaption { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
458
+ .ims-revised {
459
+ font-size: 11px;
460
+ color: var(--dsw-alias-label-tertiary);
461
+ overflow: hidden;
462
+ text-overflow: ellipsis;
463
+ white-space: nowrap;
464
+ }
465
+ .ims-detailInfo { min-width: 0; }
466
+ .ims-detailLabel { margin: 12px 0 6px; font-size: 12px; font-weight: 600; }
467
+ .ims-detailLabel:first-child { margin-top: 0; }
468
+ .ims-detailPrompt { margin: 0; font-size: 13px; white-space: pre-wrap; word-break: break-word; }
469
+ .ims-paramTable { width: 100%; border-collapse: collapse; font-size: 12px; }
470
+ .ims-paramTable td { padding: 4px 6px; border-top: 1px solid var(--dsw-alias-border-l3); word-break: break-all; }
471
+ .ims-paramTableKey { font-family: var(--ds-font-family-code); font-weight: 600; white-space: nowrap; }
472
+
473
+ /* ── 任务条 ── */
474
+ .ims-tasks {
475
+ display: flex;
476
+ flex: none;
477
+ gap: 8px;
478
+ padding: 8px 12px;
479
+ margin: 0;
480
+ overflow-x: auto;
481
+ border-top: 1px solid var(--dsw-alias-border-l3);
482
+ }
483
+ .ims-task {
484
+ display: inline-flex;
485
+ flex: none;
486
+ align-items: center;
487
+ gap: 6px;
488
+ max-width: 340px;
489
+ padding: 4px 10px;
490
+ border-radius: 999px;
491
+ background: var(--dsw-alias-bg-layer-1);
492
+ font-size: 12px;
493
+ }
494
+ .ims-taskState { font-weight: 600; flex: none; }
495
+ .ims-taskState-running { color: var(--dsw-alias-brand-primary); }
496
+ .ims-taskState-queued { color: var(--dsw-alias-label-tertiary); }
497
+ .ims-taskState-succeeded { color: var(--dsw-alias-state-success-primary); }
498
+ .ims-taskState-failed { color: var(--dsw-alias-state-error-primary); }
499
+ .ims-taskState-cancelled { color: var(--dsw-alias-label-tertiary); }
500
+ .ims-taskPrompt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
501
+ .ims-taskModel { flex: none; color: var(--dsw-alias-label-tertiary); font-size: 11px; }
502
+ .ims-taskError {
503
+ max-width: 140px;
504
+ overflow: hidden;
505
+ text-overflow: ellipsis;
506
+ white-space: nowrap;
507
+ color: var(--dsw-alias-state-error-primary);
508
+ }
509
+
510
+ /* ── 配置卡片附加件(imsc- 基础类由 cardCss('imsc') 生成)── */
511
+ .imsc-loading{padding:14px 16px;margin:0;font-size:13px;color:var(--dsw-alias-label-tertiary)}
512
+ .imsc-groupTitle { margin: 14px 0 6px; font-size: 13px; font-weight: 600; }
513
+ .imsc-presetBox {
514
+ border: 1px solid var(--dsw-alias-border-l3);
515
+ border-radius: 10px;
516
+ padding: 10px;
517
+ margin-bottom: 10px;
518
+ display: grid;
519
+ gap: 8px;
520
+ }
521
+ .imsc-presetHead { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
522
+ .imsc-presetName { font-size: 13px; font-weight: 600; }
523
+ .imsc-grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
524
+ .imsc-textarea {
525
+ box-sizing: border-box;
526
+ width: 100%;
527
+ min-height: 64px;
528
+ padding: 6px 10px;
529
+ border: 1px solid var(--dsw-alias-border-l2);
530
+ border-radius: 8px;
531
+ background: var(--dsw-alias-bg-layer-3);
532
+ color: var(--dsw-alias-label-primary);
533
+ font-family: var(--ds-font-family-code);
534
+ font-size: 12px;
535
+ resize: vertical;
536
+ }
537
+ .imsc-credRow { display: flex; align-items: center; gap: 8px; }
538
+ .imsc-credRow .imsc-input { flex: 1; min-width: 0; }
16
539
 
17
- /** 注入配置卡片占位样式。 */
18
- export function injectCardStyles(): HTMLStyleElement | null {
19
- return inject(
20
- 'dsh-plus-image-studio-card',
21
- '.dsh-plus-image-studio-card-placeholder{padding:8px;opacity:.6}',
22
- )
540
+ /* ── 移动端(≤767px,与 ui-mobile-fit 同断点)── */
541
+ @media (max-width: 767px) {
542
+ .ims-modal {
543
+ position: fixed;
544
+ inset: 0;
545
+ margin: 0;
546
+ width: 100% !important;
547
+ max-width: none !important;
548
+ height: 100%;
549
+ max-height: none;
550
+ border: none;
551
+ border-radius: 0 !important;
552
+ }
553
+ .ims-gen { grid-template-columns: minmax(0, 1fr); }
554
+ .ims-genSide { margin-top: 4px; }
555
+ .ims-tab { padding: 0 10px; }
556
+ .ims-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
557
+ .ims-detailBody { grid-template-columns: minmax(0, 1fr); }
558
+ .ims-pickerModal, .ims-detailModal {
559
+ position: fixed;
560
+ inset: 0;
561
+ width: 100% !important;
562
+ max-width: none !important;
563
+ max-height: none;
564
+ height: 100%;
565
+ border: none;
566
+ border-radius: 0 !important;
567
+ }
568
+ .ims-paramControl { width: 108px; }
569
+ .ims-btn { min-height: 40px; }
570
+ .ims-btnSmall { min-height: 32px; }
571
+ .imsc-grid2 { grid-template-columns: 1fr; }
23
572
  }
573
+ `
24
574
 
25
- function inject(id: string, css: string): HTMLStyleElement | null {
575
+ /** 注入面板 + 入口 + 卡片附加样式(重复调用幂等:已存在则跳过)。 */
576
+ export function injectStudioStyles(): HTMLStyleElement | null {
26
577
  if (typeof document === 'undefined') return null
578
+ if (document.querySelector(`style[data-plugin-css=${JSON.stringify(STYLE_TAG_ID)}]`) !== null) {
579
+ return null
580
+ }
27
581
  const tag = document.createElement('style')
28
582
  tag.dataset.plugin = PKG
29
- tag.dataset.pluginCss = `${PKG}/${id}`
30
- tag.textContent = css
583
+ tag.dataset.pluginCss = STYLE_TAG_ID
584
+ tag.textContent = imageStudioCss
31
585
  document.head.appendChild(tag)
32
586
  return tag
33
587
  }
package/src/service.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  * 红线:开发测试仅指向本地 mock,严禁真实调用产生费用。
8
8
  * @module image-studio/service
9
9
  */
10
- import { type Context, Service } from '@deepseek-ai/cordis'
10
+ import { Context, type Context as ContextT, Service } from '@deepseek-ai/cordis'
11
11
  import { credentialRef } from '@deepseek-ai/dsh-credentials'
12
12
  import type {} from '@deepseek-ai/dsh-settings'
13
13
  import { registerImageStudioApi } from './api.ts'
@@ -37,7 +37,7 @@ import { TaskRunner } from './task/runner.ts'
37
37
 
38
38
  interface SettingsLike {
39
39
  installSection(
40
- ownerCtx: Context,
40
+ ownerCtx: ContextT,
41
41
  ns: string,
42
42
  schema: unknown,
43
43
  base: unknown,
@@ -66,11 +66,13 @@ interface TaskOutcome {
66
66
  }
67
67
 
68
68
  export class ImageStudioService extends Service {
69
+ static [Context.inject] = ['credentials']
70
+
69
71
  private current: () => ImageStudioConfig
70
72
  private readonly runner: TaskRunner<TaskSnapshot>
71
73
  private readonly log: (message: string) => void
72
74
 
73
- constructor(ctx: Context, config: ImageStudioConfig) {
75
+ constructor(ctx: ContextT, config: ImageStudioConfig) {
74
76
  super(ctx, 'imageStudio')
75
77
  this.current = () => config
76
78
  this.log = (message) => ctx.logger('image-studio').warn(message)
@@ -86,7 +88,7 @@ export class ImageStudioService extends Service {
86
88
  })
87
89
  })
88
90
  ctx.inject(['webServer'], (webCtx) => {
89
- registerImageStudioApi(webCtx as Context, this)
91
+ registerImageStudioApi(webCtx as ContextT, this)
90
92
  })
91
93
  }
92
94
 
@@ -146,7 +148,7 @@ export class ImageStudioService extends Service {
146
148
  await credentials.unset(credentialRef(credentialRefNameOf(presetId)))
147
149
  }
148
150
 
149
- /** credentials seam 可选探测(缺席返回 null)。 */
151
+ /** credentials seam(inject 硬声明保证时序,见类声明;缺席即组合缺服务)。 */
150
152
  private async credentialsSeam(): Promise<CredentialsFace | null> {
151
153
  const found = (this.ctx as unknown as { get?(key: 'credentials'): unknown }).get?.(
152
154
  'credentials',
@@ -1,14 +0,0 @@
1
- /**
2
- * 独立设置页占位组件:本次交付为空壳(不做前端设计与编码)。
3
- * 后续版本在此渲染生图工作台与画廊;插槽契约已按官方 settings.section 形态声明。
4
- * @module image-studio/client/section
5
- */
6
-
7
- /** 占位组件(props 形态对齐 settings.section 四 share 注入)。 */
8
- export function StudioSection(props: { t?: (key: string) => string }): unknown {
9
- const t = props.t ?? ((key: string) => key)
10
- const el = document.createElement('div')
11
- el.className = 'dsh-plus-image-studio-section-placeholder'
12
- el.textContent = t('placeholder')
13
- return { type: el, props: {} }
14
- }