@dsh-plus/image-studio 0.1.0 → 0.1.1

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,570 @@
1
1
  /**
2
- * 画廊/生图占位样式:本次交付仅注入极简占位类名(无视觉设计)。
3
- * 沿用官方约定 <style data-plugin data-plugin-css>,HMR 按此卸载。
2
+ * 图像工作室前端样式:侧栏入口 + overlay 工作台 + 配置卡片附加件。
3
+ * 全部颜色引用 --dsw-* 设计令牌(跟随原生深浅色主题);响应式断点 767px
4
+ * 与 @dsh-plus/ui-mobile-fit 对齐(移动端面板全屏、生图表单单列、画廊两列)。
5
+ * 注入沿用 <style data-plugin data-plugin-css> 约定(HMR 可卸载)。
4
6
  * @module image-studio/client/styles
5
7
  */
6
8
 
7
9
  const PKG = '@dsh-plus/image-studio'
10
+ const STYLE_TAG_ID = `${PKG}/styles.css`
8
11
 
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
- )
12
+ export const imageStudioCss = `
13
+ /* ── 侧边栏入口(与文件/终端入口同款;flex:1 1 0 与邻居等分 footer 行宽)── */
14
+ .ims-entry {
15
+ box-sizing: border-box;
16
+ display: flex;
17
+ flex: 1 1 0;
18
+ min-width: 0;
19
+ align-items: center;
20
+ gap: 8px;
21
+ width: 100%;
22
+ height: 42px;
23
+ margin: 4px 0;
24
+ padding: 0 10px 0 8px;
25
+ border: none;
26
+ border-radius: 12px;
27
+ background: transparent;
28
+ color: var(--dsw-alias-label-primary, #1f2328);
29
+ font-family: inherit;
30
+ font-size: 14px;
31
+ line-height: 22px;
32
+ cursor: pointer;
33
+ overflow: hidden;
15
34
  }
35
+ .ims-entry:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(0,0,0,.06)); }
36
+ .ims-entry-rail {
37
+ flex: none;
38
+ width: 36px;
39
+ height: 36px;
40
+ margin: 8px 0 10px;
41
+ padding: 0;
42
+ border-radius: 50%;
43
+ justify-content: center;
44
+ gap: 0;
45
+ }
46
+ .ims-entry-label { white-space: nowrap; overflow: hidden; }
47
+
48
+ /* ── 面板骨架 ── */
49
+ .ims-backdrop {
50
+ position: fixed;
51
+ inset: 0;
52
+ z-index: 60;
53
+ display: flex;
54
+ align-items: center;
55
+ justify-content: center;
56
+ background: var(--dsw-alias-overlay-bg, rgba(0,0,0,.4));
57
+ }
58
+ .ims-panel {
59
+ display: flex;
60
+ flex-direction: column;
61
+ width: min(1100px, 94vw);
62
+ height: min(780px, 88vh);
63
+ border-radius: 16px;
64
+ background: var(--dsw-alias-bg-primary, #fff);
65
+ color: var(--dsw-alias-label-primary, #1f2328);
66
+ box-shadow: 0 16px 48px rgba(0,0,0,.24);
67
+ overflow: hidden;
68
+ }
69
+ .ims-head {
70
+ display: flex;
71
+ flex: none;
72
+ align-items: center;
73
+ gap: 8px;
74
+ padding: 10px 12px;
75
+ border-bottom: .5px solid var(--dsw-alias-border-l3, rgba(0,0,0,.08));
76
+ }
77
+ .ims-tabs { display: flex; flex: 1; gap: 4px; min-width: 0; }
78
+ .ims-tab {
79
+ display: inline-flex;
80
+ align-items: center;
81
+ gap: 6px;
82
+ height: 34px;
83
+ padding: 0 14px;
84
+ border: none;
85
+ border-radius: 10px;
86
+ background: transparent;
87
+ color: var(--dsw-alias-label-secondary, #57606a);
88
+ font-family: inherit;
89
+ font-size: 14px;
90
+ cursor: pointer;
91
+ white-space: nowrap;
92
+ }
93
+ .ims-tab:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(0,0,0,.06)); }
94
+ .ims-tabActive {
95
+ background: var(--dsw-alias-button-elevated-fill, rgba(0,0,0,.06));
96
+ color: var(--dsw-alias-label-primary, #1f2328);
97
+ font-weight: 500;
98
+ }
99
+ .ims-iconBtn {
100
+ display: inline-flex;
101
+ flex: none;
102
+ align-items: center;
103
+ justify-content: center;
104
+ width: 32px;
105
+ height: 32px;
106
+ border: none;
107
+ border-radius: 8px;
108
+ background: transparent;
109
+ color: var(--dsw-alias-label-primary, #1f2328);
110
+ cursor: pointer;
111
+ }
112
+ .ims-iconBtn:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(0,0,0,.06)); }
113
+ .ims-body { flex: 1; min-height: 0; overflow-y: auto; }
114
+
115
+ /* ── 通用件 ── */
116
+ .ims-input {
117
+ box-sizing: border-box;
118
+ width: 100%;
119
+ min-height: 34px;
120
+ padding: 6px 10px;
121
+ border: .5px solid var(--dsw-alias-border-l2, rgba(0,0,0,.16));
122
+ border-radius: 8px;
123
+ background: var(--dsw-alias-bg-primary, #fff);
124
+ color: var(--dsw-alias-label-primary, #1f2328);
125
+ font-family: inherit;
126
+ font-size: 13px;
127
+ line-height: 20px;
128
+ }
129
+ .ims-input:focus { outline: 2px solid var(--dsw-alias-focus-ring, #4d9fff); outline-offset: 0; }
130
+ .ims-btn {
131
+ display: inline-flex;
132
+ align-items: center;
133
+ justify-content: center;
134
+ gap: 6px;
135
+ min-height: 34px;
136
+ padding: 6px 14px;
137
+ border: none;
138
+ border-radius: 8px;
139
+ font-family: inherit;
140
+ font-size: 13px;
141
+ font-weight: 500;
142
+ cursor: pointer;
143
+ white-space: nowrap;
144
+ }
145
+ .ims-btn:disabled { opacity: .5; cursor: not-allowed; }
146
+ .ims-btnPrimary {
147
+ background: var(--dsw-alias-button-primary-fill, #1f2328);
148
+ color: var(--dsw-alias-button-primary-label, #fff);
149
+ }
150
+ .ims-btnGhost {
151
+ background: transparent;
152
+ color: var(--dsw-alias-label-primary, #1f2328);
153
+ border: .5px solid var(--dsw-alias-border-l2, rgba(0,0,0,.16));
154
+ }
155
+ .ims-btnGhost:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover, rgba(0,0,0,.06)); }
156
+ .ims-btnDanger { background: var(--dsw-alias-status-error, #d1242f); color: #fff; }
157
+ .ims-btnSmall { min-height: 28px; padding: 3px 10px; font-size: 12px; }
158
+ .ims-hint { margin: 4px 0 0; font-size: 12px; color: var(--dsw-alias-label-secondary, #57606a); }
159
+ .ims-empty {
160
+ padding: 32px 16px;
161
+ text-align: center;
162
+ font-size: 13px;
163
+ color: var(--dsw-alias-label-secondary, #57606a);
164
+ }
165
+ .ims-badge {
166
+ flex: none;
167
+ padding: 2px 8px;
168
+ border-radius: 999px;
169
+ font-size: 11px;
170
+ line-height: 16px;
171
+ }
172
+ .ims-badgeOn { background: var(--dsw-alias-status-success-subtle, rgba(35,134,54,.15)); color: var(--dsw-alias-status-success, #1a7f37); }
173
+ .ims-badgeOff { background: var(--dsw-alias-status-error-subtle, rgba(209,36,47,.12)); color: var(--dsw-alias-status-error, #d1242f); }
174
+ .ims-status { margin: 8px 0 0; font-size: 12px; color: var(--dsw-alias-status-success, #1a7f37); }
175
+ .ims-statusError { color: var(--dsw-alias-status-error, #d1242f); }
176
+ .ims-spin { animation: ims-rotate 1s linear infinite; }
177
+ @keyframes ims-rotate { to { transform: rotate(360deg); } }
178
+
179
+ /* ── 生图表单 ── */
180
+ .ims-gen {
181
+ display: grid;
182
+ grid-template-columns: minmax(0, 5fr) minmax(0, 4fr);
183
+ gap: 0 20px;
184
+ padding: 16px;
185
+ }
186
+ .ims-gen[hidden] { display: none; }
187
+ .ims-genMain, .ims-genSide { min-width: 0; }
188
+ .ims-block { margin-bottom: 16px; }
189
+ .ims-blockTitle { margin: 0 0 8px; font-size: 13px; font-weight: 600; }
190
+ .ims-blockHead { align-items: center; }
191
+ .ims-blockHead .ims-blockTitle { margin: 0; flex: none; }
192
+ .ims-providerRow { display: flex; align-items: center; gap: 8px; }
193
+ .ims-checkLine {
194
+ display: flex;
195
+ align-items: center;
196
+ gap: 8px;
197
+ margin-top: 10px;
198
+ font-size: 13px;
199
+ cursor: pointer;
200
+ }
201
+ .ims-inlineGrid { display: grid; gap: 8px; margin-top: 8px; }
202
+ .ims-mini { display: grid; gap: 4px; font-size: 12px; color: var(--dsw-alias-label-secondary, #57606a); }
203
+ .ims-prompt { resize: vertical; min-height: 96px; }
204
+ .ims-rowActions { display: flex; gap: 8px; margin-top: 8px; align-items: center; }
205
+ .ims-rowGrow { flex: 1; min-width: 0; }
206
+
207
+ /* 参数表单 */
208
+ .ims-paramGroup { display: grid; gap: 6px; }
209
+ .ims-paramRow {
210
+ display: flex;
211
+ align-items: center;
212
+ gap: 10px;
213
+ padding: 6px 8px;
214
+ border-radius: 8px;
215
+ }
216
+ .ims-paramRowOn { background: var(--dsw-alias-bg-secondary, rgba(0,0,0,.04)); }
217
+ .ims-paramText { flex: 1; min-width: 0; display: grid; }
218
+ .ims-paramKey { font-size: 12px; font-weight: 600; font-family: var(--ds-font-family-code, monospace); }
219
+ .ims-paramGen {
220
+ margin-left: 6px;
221
+ padding: 0 5px;
222
+ border-radius: 4px;
223
+ background: var(--dsw-alias-bg-tertiary, rgba(0,0,0,.08));
224
+ font-size: 10px;
225
+ font-weight: 400;
226
+ }
227
+ .ims-paramDesc {
228
+ font-size: 11px;
229
+ color: var(--dsw-alias-label-secondary, #57606a);
230
+ overflow: hidden;
231
+ text-overflow: ellipsis;
232
+ white-space: nowrap;
233
+ }
234
+ .ims-paramControl { width: 132px; flex: none; min-height: 30px; padding: 4px 8px; }
235
+ .ims-adv { margin-top: 8px; }
236
+ .ims-advSummary {
237
+ cursor: pointer;
238
+ font-size: 12px;
239
+ font-weight: 600;
240
+ padding: 6px 0;
241
+ color: var(--dsw-alias-label-secondary, #57606a);
242
+ user-select: none;
243
+ }
244
+ .ims-advCount {
245
+ margin-left: 6px;
246
+ padding: 0 6px;
247
+ border-radius: 999px;
248
+ background: var(--dsw-alias-button-primary-fill, #1f2328);
249
+ color: var(--dsw-alias-button-primary-label, #fff);
250
+ font-size: 10px;
251
+ }
252
+
253
+ /* 开关(switch) */
254
+ .ims-switch { position: relative; display: inline-flex; flex: none; width: 30px; height: 18px; }
255
+ .ims-switch input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
256
+ .ims-switchTrack {
257
+ position: absolute;
258
+ inset: 0;
259
+ border-radius: 999px;
260
+ background: var(--dsw-alias-border-l1, rgba(0,0,0,.24));
261
+ transition: background .15s;
262
+ pointer-events: none;
263
+ }
264
+ .ims-switchTrack::after {
265
+ content: '';
266
+ position: absolute;
267
+ top: 2px;
268
+ left: 2px;
269
+ width: 14px;
270
+ height: 14px;
271
+ border-radius: 50%;
272
+ background: #fff;
273
+ transition: transform .15s;
274
+ }
275
+ .ims-switch input:checked + .ims-switchTrack { background: var(--dsw-alias-status-success, #1a7f37); }
276
+ .ims-switch input:checked + .ims-switchTrack::after { transform: translateX(12px); }
277
+
278
+ /* 源图/遮罩缩略 */
279
+ .ims-sources { display: flex; flex-wrap: wrap; gap: 8px; }
280
+ .ims-thumb { position: relative; width: 64px; height: 64px; border-radius: 8px; overflow: hidden; }
281
+ .ims-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
282
+ .ims-thumbRemove {
283
+ position: absolute;
284
+ top: 2px;
285
+ right: 2px;
286
+ width: 18px;
287
+ height: 18px;
288
+ border: none;
289
+ border-radius: 50%;
290
+ background: rgba(0,0,0,.55);
291
+ color: #fff;
292
+ font-size: 12px;
293
+ line-height: 1;
294
+ cursor: pointer;
295
+ }
296
+ .ims-thumbAdd {
297
+ display: inline-flex;
298
+ flex-direction: column;
299
+ align-items: center;
300
+ justify-content: center;
301
+ gap: 4px;
302
+ width: 64px;
303
+ height: 64px;
304
+ border: 1px dashed var(--dsw-alias-border-l1, rgba(0,0,0,.24));
305
+ border-radius: 8px;
306
+ background: transparent;
307
+ color: var(--dsw-alias-label-secondary, #57606a);
308
+ font-size: 11px;
309
+ cursor: pointer;
310
+ }
311
+ .ims-thumbAdd:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(0,0,0,.06)); }
312
+
313
+ /* 提交区 */
314
+ .ims-genFoot { grid-column: 1 / -1; border-top: .5px solid var(--dsw-alias-border-l3, rgba(0,0,0,.08)); padding-top: 12px; }
315
+ .ims-naming { display: flex; gap: 8px; margin-bottom: 8px; }
316
+ .ims-submit { width: 100%; min-height: 40px; font-size: 14px; margin-top: 8px; }
317
+
318
+ /* ── 图片选择器 ── */
319
+ .ims-pickerBackdrop, .ims-detailBackdrop {
320
+ position: absolute;
321
+ inset: 0;
322
+ z-index: 5;
323
+ display: flex;
324
+ align-items: center;
325
+ justify-content: center;
326
+ background: var(--dsw-alias-overlay-bg, rgba(0,0,0,.4));
327
+ }
328
+ .ims-picker, .ims-detail {
329
+ display: flex;
330
+ flex-direction: column;
331
+ width: min(760px, 92%);
332
+ max-height: 86%;
333
+ border-radius: 14px;
334
+ background: var(--dsw-alias-bg-primary, #fff);
335
+ overflow: hidden;
336
+ }
337
+ .ims-pickerHead, .ims-detailHead {
338
+ display: flex;
339
+ flex: none;
340
+ align-items: center;
341
+ justify-content: space-between;
342
+ gap: 8px;
343
+ padding: 10px 14px;
344
+ border-bottom: .5px solid var(--dsw-alias-border-l3, rgba(0,0,0,.08));
345
+ }
346
+ .ims-pickerTitle { margin: 0; font-size: 14px; }
347
+ .ims-pickerGrid {
348
+ flex: 1;
349
+ min-height: 0;
350
+ overflow-y: auto;
351
+ display: grid;
352
+ grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
353
+ gap: 8px;
354
+ padding: 12px 14px;
355
+ }
356
+ .ims-pickerCell {
357
+ position: relative;
358
+ aspect-ratio: 1;
359
+ border: 2px solid transparent;
360
+ border-radius: 8px;
361
+ padding: 0;
362
+ background: none;
363
+ cursor: pointer;
364
+ overflow: hidden;
365
+ }
366
+ .ims-pickerCell img { width: 100%; height: 100%; object-fit: cover; display: block; }
367
+ .ims-pickerCellOn { border-color: var(--dsw-alias-focus-ring, #4d9fff); }
368
+ .ims-pickerMark {
369
+ position: absolute;
370
+ top: 4px;
371
+ right: 4px;
372
+ min-width: 18px;
373
+ height: 18px;
374
+ border-radius: 999px;
375
+ background: var(--dsw-alias-button-primary-fill, #1f2328);
376
+ color: var(--dsw-alias-button-primary-label, #fff);
377
+ font-size: 11px;
378
+ line-height: 18px;
379
+ text-align: center;
380
+ }
381
+ .ims-pickerFoot {
382
+ display: flex;
383
+ flex: none;
384
+ justify-content: flex-end;
385
+ gap: 8px;
386
+ padding: 10px 14px;
387
+ border-top: .5px solid var(--dsw-alias-border-l3, rgba(0,0,0,.08));
388
+ }
389
+
390
+ /* ── 画廊 ── */
391
+ .ims-gallery { padding: 12px 16px; }
392
+ .ims-gallery[hidden] { display: none; }
393
+ .ims-galleryBar { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
394
+ .ims-grid {
395
+ display: grid;
396
+ grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
397
+ gap: 10px;
398
+ }
399
+ .ims-cell {
400
+ position: relative;
401
+ aspect-ratio: 1;
402
+ border: none;
403
+ border-radius: 10px;
404
+ padding: 0;
405
+ background: var(--dsw-alias-bg-secondary, rgba(0,0,0,.04));
406
+ cursor: pointer;
407
+ overflow: hidden;
408
+ }
409
+ .ims-cell img { width: 100%; height: 100%; object-fit: cover; display: block; }
410
+ .ims-cellPrompt {
411
+ position: absolute;
412
+ left: 0;
413
+ right: 0;
414
+ bottom: 0;
415
+ padding: 14px 8px 6px;
416
+ background: linear-gradient(transparent, rgba(0,0,0,.72));
417
+ color: #fff;
418
+ font-size: 11px;
419
+ text-align: left;
420
+ overflow: hidden;
421
+ text-overflow: ellipsis;
422
+ white-space: nowrap;
423
+ opacity: 0;
424
+ transition: opacity .15s;
425
+ }
426
+ .ims-cell:hover .ims-cellPrompt, .ims-cell:focus-visible .ims-cellPrompt { opacity: 1; }
427
+
428
+ /* 详情模态 */
429
+ .ims-detail { width: min(920px, 94%); }
430
+ .ims-detailMeta { font-size: 12px; color: var(--dsw-alias-label-secondary, #57606a); }
431
+ .ims-detailBody {
432
+ flex: 1;
433
+ min-height: 0;
434
+ overflow-y: auto;
435
+ display: grid;
436
+ grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
437
+ gap: 16px;
438
+ padding: 14px;
439
+ }
440
+ .ims-detailImages { display: grid; gap: 10px; align-content: start; }
441
+ .ims-detailFigure { margin: 0; display: grid; gap: 6px; }
442
+ .ims-detailFigure img {
443
+ width: 100%;
444
+ border-radius: 10px;
445
+ background: var(--dsw-alias-bg-secondary, rgba(0,0,0,.04));
446
+ display: block;
447
+ }
448
+ .ims-detailFigure figcaption { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
449
+ .ims-revised {
450
+ font-size: 11px;
451
+ color: var(--dsw-alias-label-secondary, #57606a);
452
+ overflow: hidden;
453
+ text-overflow: ellipsis;
454
+ white-space: nowrap;
455
+ }
456
+ .ims-detailInfo { min-width: 0; }
457
+ .ims-detailLabel { margin: 12px 0 6px; font-size: 12px; font-weight: 600; }
458
+ .ims-detailLabel:first-child { margin-top: 0; }
459
+ .ims-detailPrompt { margin: 0; font-size: 13px; white-space: pre-wrap; word-break: break-word; }
460
+ .ims-paramTable { width: 100%; border-collapse: collapse; font-size: 12px; }
461
+ .ims-paramTable td { padding: 4px 6px; border-top: .5px solid var(--dsw-alias-border-l3, rgba(0,0,0,.08)); word-break: break-all; }
462
+ .ims-paramTableKey { font-family: var(--ds-font-family-code, monospace); font-weight: 600; white-space: nowrap; }
463
+ .ims-detailFoot {
464
+ display: flex;
465
+ flex: none;
466
+ align-items: center;
467
+ justify-content: flex-end;
468
+ gap: 8px;
469
+ padding: 10px 14px;
470
+ border-top: .5px solid var(--dsw-alias-border-l3, rgba(0,0,0,.08));
471
+ }
472
+ .ims-detailFoot .ims-status { margin: 0 auto 0 0; }
473
+
474
+ /* ── 任务条 ── */
475
+ .ims-tasks {
476
+ display: flex;
477
+ flex: none;
478
+ gap: 8px;
479
+ padding: 8px 12px;
480
+ overflow-x: auto;
481
+ border-top: .5px solid var(--dsw-alias-border-l3, rgba(0,0,0,.08));
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-secondary, rgba(0,0,0,.04));
492
+ font-size: 12px;
493
+ }
494
+ .ims-taskState { font-weight: 600; flex: none; }
495
+ .ims-taskState-running { color: var(--dsw-alias-status-info, #0969da); }
496
+ .ims-taskState-queued { color: var(--dsw-alias-label-secondary, #57606a); }
497
+ .ims-taskState-succeeded { color: var(--dsw-alias-status-success, #1a7f37); }
498
+ .ims-taskState-failed { color: var(--dsw-alias-status-error, #d1242f); }
499
+ .ims-taskState-cancelled { color: var(--dsw-alias-label-secondary, #57606a); }
500
+ .ims-taskPrompt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
501
+ .ims-taskModel { flex: none; color: var(--dsw-alias-label-secondary, #57606a); 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-status-error, #d1242f);
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: .5px solid var(--dsw-alias-border-l3, rgba(0,0,0,.08));
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: .5px solid var(--dsw-alias-border-l2, rgba(0,0,0,.16));
530
+ border-radius: 8px;
531
+ background: var(--dsw-alias-bg-primary, #fff);
532
+ color: var(--dsw-alias-label-primary, #1f2328);
533
+ font-family: var(--ds-font-family-code, monospace);
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-backdrop { align-items: stretch; justify-content: stretch; }
543
+ .ims-panel { width: 100%; height: 100%; border-radius: 0; }
544
+ .ims-gen { grid-template-columns: minmax(0, 1fr); }
545
+ .ims-genSide { margin-top: 4px; }
546
+ .ims-tab { padding: 0 10px; }
547
+ .ims-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
548
+ .ims-detailBody { grid-template-columns: minmax(0, 1fr); }
549
+ .ims-picker, .ims-detail { width: 100%; max-height: 100%; height: 100%; border-radius: 0; }
550
+ .ims-pickerBackdrop, .ims-detailBackdrop { align-items: stretch; justify-content: stretch; }
551
+ .ims-paramControl { width: 108px; }
552
+ .ims-btn { min-height: 40px; }
553
+ .ims-btnSmall { min-height: 32px; }
554
+ .imsc-grid2 { grid-template-columns: 1fr; }
23
555
  }
556
+ `
24
557
 
25
- function inject(id: string, css: string): HTMLStyleElement | null {
558
+ /** 注入面板 + 入口 + 卡片附加样式(重复调用幂等:已存在则跳过)。 */
559
+ export function injectStudioStyles(): HTMLStyleElement | null {
26
560
  if (typeof document === 'undefined') return null
561
+ if (document.querySelector(`style[data-plugin-css=${JSON.stringify(STYLE_TAG_ID)}]`) !== null) {
562
+ return null
563
+ }
27
564
  const tag = document.createElement('style')
28
565
  tag.dataset.plugin = PKG
29
- tag.dataset.pluginCss = `${PKG}/${id}`
30
- tag.textContent = css
566
+ tag.dataset.pluginCss = STYLE_TAG_ID
567
+ tag.textContent = imageStudioCss
31
568
  document.head.appendChild(tag)
32
569
  return tag
33
570
  }
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
- }