@goodandready/dsh-image-gen 0.10.18 → 0.10.20
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.md +19 -4
- package/README.ru.md +19 -5
- package/README.zh.md +18 -0
- package/lib/client.js +655 -1
- package/lib/index.js +39 -1
- package/lib/prompt-polisher.js +194 -0
- package/lib/providers.js +84 -0
- package/lib/tools/editing.js +8 -3
- package/lib/tools/generation.js +11 -1
- package/lib/tools/processing.js +172 -0
- package/package.json +3 -3
package/lib/client.js
CHANGED
|
@@ -503,6 +503,397 @@ window.__ModuleLoader__.load({
|
|
|
503
503
|
)
|
|
504
504
|
}
|
|
505
505
|
|
|
506
|
+
|
|
507
|
+
// -------------------------------------------------------------- Diagnostics Panel
|
|
508
|
+
function DiagnosticsPanel(props) {
|
|
509
|
+
const t = props.t || ((k) => k)
|
|
510
|
+
const [testing, setTesting] = react.useState(false)
|
|
511
|
+
const [result, setResult] = react.useState(null)
|
|
512
|
+
|
|
513
|
+
const handleTest = async () => {
|
|
514
|
+
setTesting(true)
|
|
515
|
+
setResult(null)
|
|
516
|
+
try {
|
|
517
|
+
const res = await fetch('/dsh-image-gen/diagnostics/test?provider=' + encodeURIComponent(props.provider || 'fal'))
|
|
518
|
+
const data = await res.json()
|
|
519
|
+
setResult(data)
|
|
520
|
+
} catch (e) {
|
|
521
|
+
setResult({ ok: false, message: e.message })
|
|
522
|
+
} finally {
|
|
523
|
+
setTesting(false)
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
return react.createElement(
|
|
528
|
+
'div',
|
|
529
|
+
{
|
|
530
|
+
style: {
|
|
531
|
+
padding: '10px 14px',
|
|
532
|
+
border: '1px solid var(--dsw-alias-border-l2)',
|
|
533
|
+
borderRadius: '8px',
|
|
534
|
+
background: 'var(--dsw-alias-bg-layer-2)',
|
|
535
|
+
display: 'flex',
|
|
536
|
+
alignItems: 'center',
|
|
537
|
+
justifyContent: 'space-between',
|
|
538
|
+
marginBottom: '12px',
|
|
539
|
+
},
|
|
540
|
+
},
|
|
541
|
+
react.createElement(
|
|
542
|
+
'div',
|
|
543
|
+
{ style: { display: 'flex', flexDirection: 'column', gap: '2px' } },
|
|
544
|
+
react.createElement('span', { style: { fontSize: '13px', fontWeight: '600', color: 'var(--dsw-alias-label-primary)' } }, t('diagnostics.title')),
|
|
545
|
+
result
|
|
546
|
+
? react.createElement(
|
|
547
|
+
'span',
|
|
548
|
+
{
|
|
549
|
+
style: {
|
|
550
|
+
fontSize: '12px',
|
|
551
|
+
fontWeight: '500',
|
|
552
|
+
color: result.ok ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-state-error-primary)',
|
|
553
|
+
},
|
|
554
|
+
},
|
|
555
|
+
(result.ok ? '🟢 ' : '🔴 ') + (result.message || (result.ok ? t('diagnostics.ready') : 'Failed')) + (result.latencyMs ? ' (' + result.latencyMs + 'ms)' : '')
|
|
556
|
+
)
|
|
557
|
+
: react.createElement('span', { style: { fontSize: '12px', color: 'var(--dsw-alias-label-secondary)' } }, t('diagnostics.desc'))
|
|
558
|
+
),
|
|
559
|
+
react.createElement(
|
|
560
|
+
'button',
|
|
561
|
+
{
|
|
562
|
+
type: 'button',
|
|
563
|
+
disabled: testing,
|
|
564
|
+
onClick: handleTest,
|
|
565
|
+
className: 'ig-save-btn',
|
|
566
|
+
style: { padding: '5px 12px', fontSize: '12px', opacity: testing ? 0.7 : 1 },
|
|
567
|
+
},
|
|
568
|
+
testing ? t('diagnostics.testing') : t('diagnostics.test')
|
|
569
|
+
)
|
|
570
|
+
)
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// -------------------------------------------------------------- Gallery View
|
|
574
|
+
function GalleryView(props) {
|
|
575
|
+
const t = props.t || ((k) => k)
|
|
576
|
+
const [items, setItems] = react.useState([])
|
|
577
|
+
const [loading, setLoading] = react.useState(true)
|
|
578
|
+
const [selected, setSelected] = react.useState(null)
|
|
579
|
+
const [copiedPrompt, setCopiedPrompt] = react.useState(false)
|
|
580
|
+
const [copiedLink, setCopiedLink] = react.useState(false)
|
|
581
|
+
|
|
582
|
+
const loadHistory = () => {
|
|
583
|
+
setLoading(true)
|
|
584
|
+
fetch('/dsh-image-gen/history')
|
|
585
|
+
.then((r) => r.json())
|
|
586
|
+
.then((data) => {
|
|
587
|
+
setItems(Array.isArray(data) ? data : [])
|
|
588
|
+
setLoading(false)
|
|
589
|
+
})
|
|
590
|
+
.catch(() => {
|
|
591
|
+
setItems([])
|
|
592
|
+
setLoading(false)
|
|
593
|
+
})
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
react.useEffect(() => {
|
|
597
|
+
loadHistory()
|
|
598
|
+
}, [])
|
|
599
|
+
|
|
600
|
+
if (loading) {
|
|
601
|
+
return react.createElement(
|
|
602
|
+
'div',
|
|
603
|
+
{ style: { padding: '24px', textAlign: 'center', color: 'var(--dsw-alias-label-secondary)', fontSize: '13px' } },
|
|
604
|
+
'Loading recent generations...'
|
|
605
|
+
)
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
if (items.length === 0) {
|
|
609
|
+
return react.createElement(
|
|
610
|
+
'div',
|
|
611
|
+
{
|
|
612
|
+
style: {
|
|
613
|
+
padding: '32px 16px',
|
|
614
|
+
textAlign: 'center',
|
|
615
|
+
color: 'var(--dsw-alias-label-secondary)',
|
|
616
|
+
fontSize: '13px',
|
|
617
|
+
display: 'flex',
|
|
618
|
+
flexDirection: 'column',
|
|
619
|
+
alignItems: 'center',
|
|
620
|
+
gap: '8px',
|
|
621
|
+
},
|
|
622
|
+
},
|
|
623
|
+
react.createElement('span', { style: { fontSize: '24px' } }, '🖼️'),
|
|
624
|
+
react.createElement('p', null, t('gallery.empty')),
|
|
625
|
+
react.createElement(
|
|
626
|
+
'button',
|
|
627
|
+
{
|
|
628
|
+
type: 'button',
|
|
629
|
+
onClick: loadHistory,
|
|
630
|
+
className: 'ig-tab-btn',
|
|
631
|
+
style: { border: '1px solid var(--dsw-alias-border-l2)', marginTop: '6px' },
|
|
632
|
+
},
|
|
633
|
+
'🔄 Refresh'
|
|
634
|
+
)
|
|
635
|
+
)
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
return react.createElement(
|
|
639
|
+
'div',
|
|
640
|
+
{ style: { display: 'flex', flexDirection: 'column', gap: '12px', width: '100%' } },
|
|
641
|
+
react.createElement(
|
|
642
|
+
'div',
|
|
643
|
+
{ style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', paddingBottom: '6px' } },
|
|
644
|
+
react.createElement('span', { style: { fontSize: '13px', fontWeight: '600', color: 'var(--dsw-alias-label-primary)' } }, t('gallery.title') + ' (' + items.length + ')'),
|
|
645
|
+
react.createElement('button', { type: 'button', onClick: loadHistory, className: 'ig-tab-btn', style: { padding: '3px 8px', fontSize: '12px' } }, '🔄 Refresh')
|
|
646
|
+
),
|
|
647
|
+
react.createElement(
|
|
648
|
+
'div',
|
|
649
|
+
{
|
|
650
|
+
style: {
|
|
651
|
+
display: 'grid',
|
|
652
|
+
gridTemplateColumns: 'repeat(auto-fill, minmax(130px, 1fr))',
|
|
653
|
+
gap: '10px',
|
|
654
|
+
maxHeight: '480px',
|
|
655
|
+
overflowY: 'auto',
|
|
656
|
+
padding: '2px',
|
|
657
|
+
},
|
|
658
|
+
},
|
|
659
|
+
items.map((item, idx) => {
|
|
660
|
+
const thumb = item.thumbnailUrl || (item.attachmentId ? '/dsh-image-gen/image?id=' + encodeURIComponent(item.attachmentId) : '')
|
|
661
|
+
return react.createElement(
|
|
662
|
+
'div',
|
|
663
|
+
{
|
|
664
|
+
key: item.attachmentId || item.path || idx,
|
|
665
|
+
onClick: () => setSelected(item),
|
|
666
|
+
style: {
|
|
667
|
+
position: 'relative',
|
|
668
|
+
borderRadius: '8px',
|
|
669
|
+
overflow: 'hidden',
|
|
670
|
+
border: '1px solid var(--dsw-alias-border-l2)',
|
|
671
|
+
background: 'var(--dsw-alias-bg-layer-2)',
|
|
672
|
+
cursor: 'pointer',
|
|
673
|
+
aspectRatio: '1',
|
|
674
|
+
display: 'flex',
|
|
675
|
+
alignItems: 'center',
|
|
676
|
+
justifyContent: 'center',
|
|
677
|
+
},
|
|
678
|
+
},
|
|
679
|
+
thumb
|
|
680
|
+
? react.createElement('img', {
|
|
681
|
+
src: thumb,
|
|
682
|
+
alt: item.prompt || 'Generated image',
|
|
683
|
+
loading: 'lazy',
|
|
684
|
+
style: { width: '100%', height: '100%', objectFit: 'cover' },
|
|
685
|
+
})
|
|
686
|
+
: react.createElement('span', { style: { fontSize: '12px', color: 'var(--dsw-alias-label-secondary)' } }, '🖼️'),
|
|
687
|
+
react.createElement(
|
|
688
|
+
'div',
|
|
689
|
+
{
|
|
690
|
+
style: {
|
|
691
|
+
position: 'absolute',
|
|
692
|
+
bottom: 0,
|
|
693
|
+
left: 0,
|
|
694
|
+
right: 0,
|
|
695
|
+
padding: '4px 6px',
|
|
696
|
+
background: 'linear-gradient(transparent, rgba(0,0,0,0.8))',
|
|
697
|
+
fontSize: '10px',
|
|
698
|
+
color: '#fff',
|
|
699
|
+
whiteSpace: 'nowrap',
|
|
700
|
+
overflow: 'hidden',
|
|
701
|
+
textOverflow: 'ellipsis',
|
|
702
|
+
},
|
|
703
|
+
},
|
|
704
|
+
item.prompt || 'Untitled'
|
|
705
|
+
)
|
|
706
|
+
)
|
|
707
|
+
})
|
|
708
|
+
),
|
|
709
|
+
selected &&
|
|
710
|
+
react.createElement(
|
|
711
|
+
'div',
|
|
712
|
+
{
|
|
713
|
+
style: {
|
|
714
|
+
position: 'fixed',
|
|
715
|
+
inset: 0,
|
|
716
|
+
background: 'rgba(0,0,0,0.7)',
|
|
717
|
+
zIndex: 10000,
|
|
718
|
+
display: 'flex',
|
|
719
|
+
alignItems: 'center',
|
|
720
|
+
justifyContent: 'center',
|
|
721
|
+
padding: '20px',
|
|
722
|
+
},
|
|
723
|
+
onClick: () => setSelected(null),
|
|
724
|
+
},
|
|
725
|
+
react.createElement(
|
|
726
|
+
'div',
|
|
727
|
+
{
|
|
728
|
+
style: {
|
|
729
|
+
background: 'var(--dsw-alias-bg-layer-3)',
|
|
730
|
+
border: '1px solid var(--dsw-alias-border-l2)',
|
|
731
|
+
borderRadius: '12px',
|
|
732
|
+
maxWidth: '560px',
|
|
733
|
+
width: '100%',
|
|
734
|
+
maxHeight: '90vh',
|
|
735
|
+
overflowY: 'auto',
|
|
736
|
+
padding: '18px',
|
|
737
|
+
display: 'flex',
|
|
738
|
+
flexDirection: 'column',
|
|
739
|
+
gap: '12px',
|
|
740
|
+
},
|
|
741
|
+
onClick: (e) => e.stopPropagation(),
|
|
742
|
+
},
|
|
743
|
+
react.createElement(
|
|
744
|
+
'div',
|
|
745
|
+
{ style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' } },
|
|
746
|
+
react.createElement('span', { style: { fontWeight: '700', fontSize: '14px', color: 'var(--dsw-alias-label-primary)' } }, t('gallery.inspector')),
|
|
747
|
+
react.createElement(
|
|
748
|
+
'button',
|
|
749
|
+
{
|
|
750
|
+
type: 'button',
|
|
751
|
+
onClick: () => setSelected(null),
|
|
752
|
+
style: { background: 'none', border: 'none', cursor: 'pointer', fontSize: '16px', color: 'var(--dsw-alias-label-secondary)' },
|
|
753
|
+
},
|
|
754
|
+
'✕'
|
|
755
|
+
)
|
|
756
|
+
),
|
|
757
|
+
react.createElement('img', {
|
|
758
|
+
src: selected.thumbnailUrl || (selected.attachmentId ? '/dsh-image-gen/image?id=' + encodeURIComponent(selected.attachmentId) : ''),
|
|
759
|
+
style: { width: '100%', borderRadius: '8px', maxHeight: '340px', objectFit: 'contain', background: '#0a0a0c' },
|
|
760
|
+
}),
|
|
761
|
+
react.createElement(
|
|
762
|
+
'div',
|
|
763
|
+
{ style: { display: 'flex', flexWrap: 'wrap', gap: '6px', fontSize: '11px' } },
|
|
764
|
+
selected.seed !== undefined && react.createElement('span', { className: 'ig-badge' }, 'Seed: ' + selected.seed),
|
|
765
|
+
selected.provider && react.createElement('span', { className: 'ig-badge' }, 'Provider: ' + selected.provider),
|
|
766
|
+
selected.width && react.createElement('span', { className: 'ig-badge' }, selected.width + '×' + selected.height),
|
|
767
|
+
selected.cost ? react.createElement('span', { className: 'ig-badge' }, '$' + Number(selected.cost).toFixed(4)) : null
|
|
768
|
+
),
|
|
769
|
+
react.createElement(
|
|
770
|
+
'div',
|
|
771
|
+
{ style: { display: 'flex', flexDirection: 'column', gap: '4px' } },
|
|
772
|
+
react.createElement('span', { style: { fontSize: '11px', fontWeight: '600', color: 'var(--dsw-alias-label-secondary)' } }, 'Prompt:'),
|
|
773
|
+
react.createElement(
|
|
774
|
+
'p',
|
|
775
|
+
{
|
|
776
|
+
style: {
|
|
777
|
+
fontSize: '12px',
|
|
778
|
+
color: 'var(--dsw-alias-label-primary)',
|
|
779
|
+
lineHeight: '1.4',
|
|
780
|
+
background: 'var(--dsw-alias-bg-layer-2)',
|
|
781
|
+
padding: '8px',
|
|
782
|
+
borderRadius: '6px',
|
|
783
|
+
margin: 0,
|
|
784
|
+
},
|
|
785
|
+
},
|
|
786
|
+
selected.prompt || 'No prompt recorded'
|
|
787
|
+
)
|
|
788
|
+
),
|
|
789
|
+
react.createElement(
|
|
790
|
+
'div',
|
|
791
|
+
{ style: { display: 'flex', gap: '8px', justifyContent: 'flex-end', marginTop: '4px' } },
|
|
792
|
+
react.createElement(
|
|
793
|
+
'button',
|
|
794
|
+
{
|
|
795
|
+
type: 'button',
|
|
796
|
+
className: 'ig-tab-btn',
|
|
797
|
+
style: { border: '1px solid var(--dsw-alias-border-l2)' },
|
|
798
|
+
onClick: () => {
|
|
799
|
+
if (typeof navigator !== 'undefined' && navigator.clipboard && selected.prompt) {
|
|
800
|
+
navigator.clipboard.writeText(selected.prompt)
|
|
801
|
+
setCopiedPrompt(true)
|
|
802
|
+
setTimeout(() => setCopiedPrompt(false), 2000)
|
|
803
|
+
}
|
|
804
|
+
},
|
|
805
|
+
},
|
|
806
|
+
copiedPrompt ? '✓ Copied!' : t('gallery.copy_prompt')
|
|
807
|
+
),
|
|
808
|
+
react.createElement(
|
|
809
|
+
'button',
|
|
810
|
+
{
|
|
811
|
+
type: 'button',
|
|
812
|
+
className: 'ig-save-btn',
|
|
813
|
+
onClick: () => {
|
|
814
|
+
const url = selected.thumbnailUrl || (selected.attachmentId ? '/dsh-image-gen/image?id=' + encodeURIComponent(selected.attachmentId) : '')
|
|
815
|
+
if (typeof navigator !== 'undefined' && navigator.clipboard && url) {
|
|
816
|
+
navigator.clipboard.writeText('')
|
|
817
|
+
setCopiedLink(true)
|
|
818
|
+
setTimeout(() => setCopiedLink(false), 2000)
|
|
819
|
+
}
|
|
820
|
+
},
|
|
821
|
+
},
|
|
822
|
+
copiedLink ? '✓ Copied!' : t('gallery.copy_link')
|
|
823
|
+
)
|
|
824
|
+
)
|
|
825
|
+
)
|
|
826
|
+
)
|
|
827
|
+
)
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// -------------------------------------------------------------- Header Quick-Access Chip
|
|
831
|
+
function GalleryHeaderChip(props) {
|
|
832
|
+
const [open, setOpen] = react.useState(false)
|
|
833
|
+
return react.createElement(
|
|
834
|
+
'div',
|
|
835
|
+
{ style: { display: 'inline-flex', alignItems: 'center', position: 'relative' } },
|
|
836
|
+
react.createElement(
|
|
837
|
+
'button',
|
|
838
|
+
{
|
|
839
|
+
type: 'button',
|
|
840
|
+
title: 'Image Studio Gallery',
|
|
841
|
+
onClick: () => setOpen(!open),
|
|
842
|
+
style: {
|
|
843
|
+
appearance: 'none',
|
|
844
|
+
background: 'none',
|
|
845
|
+
border: '1px solid var(--dsw-alias-border-l2)',
|
|
846
|
+
borderRadius: '6px',
|
|
847
|
+
padding: '4px 8px',
|
|
848
|
+
cursor: 'pointer',
|
|
849
|
+
display: 'flex',
|
|
850
|
+
alignItems: 'center',
|
|
851
|
+
gap: '5px',
|
|
852
|
+
fontSize: '12px',
|
|
853
|
+
color: 'var(--dsw-alias-label-secondary)',
|
|
854
|
+
},
|
|
855
|
+
},
|
|
856
|
+
react.createElement('span', null, '🖼️'),
|
|
857
|
+
react.createElement('span', null, 'Gallery')
|
|
858
|
+
),
|
|
859
|
+
open &&
|
|
860
|
+
react.createElement(
|
|
861
|
+
'div',
|
|
862
|
+
{
|
|
863
|
+
style: {
|
|
864
|
+
position: 'fixed',
|
|
865
|
+
top: '56px',
|
|
866
|
+
right: '16px',
|
|
867
|
+
width: '380px',
|
|
868
|
+
maxHeight: '520px',
|
|
869
|
+
background: 'var(--dsw-alias-bg-layer-3)',
|
|
870
|
+
border: '1px solid var(--dsw-alias-border-l2)',
|
|
871
|
+
borderRadius: '12px',
|
|
872
|
+
boxShadow: '0 8px 32px rgba(0,0,0,0.4)',
|
|
873
|
+
zIndex: 9999,
|
|
874
|
+
padding: '16px',
|
|
875
|
+
overflowY: 'auto',
|
|
876
|
+
},
|
|
877
|
+
},
|
|
878
|
+
react.createElement(
|
|
879
|
+
'div',
|
|
880
|
+
{ style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '10px' } },
|
|
881
|
+
react.createElement('span', { style: { fontWeight: '700', fontSize: '14px', color: 'var(--dsw-alias-label-primary)' } }, 'Image Studio Gallery'),
|
|
882
|
+
react.createElement(
|
|
883
|
+
'button',
|
|
884
|
+
{
|
|
885
|
+
type: 'button',
|
|
886
|
+
onClick: () => setOpen(false),
|
|
887
|
+
style: { background: 'none', border: 'none', cursor: 'pointer', fontSize: '14px', color: 'var(--dsw-alias-label-secondary)' },
|
|
888
|
+
},
|
|
889
|
+
'✕'
|
|
890
|
+
)
|
|
891
|
+
),
|
|
892
|
+
react.createElement(GalleryView, { ctx: props.ctx })
|
|
893
|
+
)
|
|
894
|
+
)
|
|
895
|
+
}
|
|
896
|
+
|
|
506
897
|
function FalSettingsCard(props) {
|
|
507
898
|
const t = props.t || ((k) => k)
|
|
508
899
|
const [open, setOpen] = react.useState(props.defaultOpen ?? true)
|
|
@@ -538,6 +929,7 @@ window.__ModuleLoader__.load({
|
|
|
538
929
|
const tabs = [
|
|
539
930
|
{ id: 'general', label: t('tab.general'), icon: '⚙️' },
|
|
540
931
|
{ id: 'provider', label: t('tab.provider') + ' (' + currentProvider.toUpperCase() + ')', icon: '🔌' },
|
|
932
|
+
{ id: 'gallery', label: t('tab.gallery'), icon: '🖼️' },
|
|
541
933
|
{ id: 'enhancer', label: t('tab.enhancer'), icon: '✨' },
|
|
542
934
|
{ id: 'safety', label: t('tab.safety'), icon: '🛡️' },
|
|
543
935
|
{ id: 'cache', label: t('tab.cache'), icon: '⚡' },
|
|
@@ -1037,6 +1429,177 @@ window.__ModuleLoader__.load({
|
|
|
1037
1429
|
'p.cacheTtlDays': '0',
|
|
1038
1430
|
}
|
|
1039
1431
|
|
|
1432
|
+
const zh = {
|
|
1433
|
+
'tab.general': '常规',
|
|
1434
|
+
'tab.provider': '提供商',
|
|
1435
|
+
'tab.gallery': '图库',
|
|
1436
|
+
'tab.enhancer': '提示词增强',
|
|
1437
|
+
'tab.safety': '安全与预算',
|
|
1438
|
+
'tab.cache': '缓存与性能',
|
|
1439
|
+
|
|
1440
|
+
'diagnostics.title': '提供商连接诊断',
|
|
1441
|
+
'diagnostics.desc': '即时检查与所选生成服务 API 的连接状态和延迟。',
|
|
1442
|
+
'diagnostics.test': '测试连接',
|
|
1443
|
+
'diagnostics.testing': '测试中...',
|
|
1444
|
+
'diagnostics.ready': '正常',
|
|
1445
|
+
|
|
1446
|
+
'gallery.title': '最近生成记录',
|
|
1447
|
+
'gallery.empty': '暂无生成记录。生成一张图片后将显示在此处。',
|
|
1448
|
+
'gallery.inspector': '生成记录详情',
|
|
1449
|
+
'gallery.copy_prompt': '复制提示词',
|
|
1450
|
+
'gallery.copy_link': '复制 Markdown 链接',
|
|
1451
|
+
|
|
1452
|
+
'title': 'AI 图像工坊',
|
|
1453
|
+
'subtitle': '多提供商支持的图像生成、编辑、局部重绘与矢量化。',
|
|
1454
|
+
'card.title': 'AI 图像工坊',
|
|
1455
|
+
'card.desc': '配置多提供商图像生成、局部重绘、放大及矢量化设置。',
|
|
1456
|
+
'card.dirty': '有未保存的修改',
|
|
1457
|
+
'card.saving': '保存中...',
|
|
1458
|
+
'card.save': '保存修改',
|
|
1459
|
+
'card.discard': '放弃修改',
|
|
1460
|
+
'card.failed': '保存失败,请检查填写内容。',
|
|
1461
|
+
|
|
1462
|
+
'settings.overridden': '已自定义',
|
|
1463
|
+
'settings.reset': '重置',
|
|
1464
|
+
'settings.invalidNumber': '请输入有效数值',
|
|
1465
|
+
'settings.unavailable': '设置命名空间当前不可用。',
|
|
1466
|
+
|
|
1467
|
+
'f.enabled': '启用图像生成',
|
|
1468
|
+
'f.enabledHint': '关闭后将禁用所有图像生成及处理工具。',
|
|
1469
|
+
'f.provider': '默认图像提供商',
|
|
1470
|
+
'f.providerHint': '用于图像生成的默认引擎(fal, custom, replicate, seedream, gemini, local, codex, grok)。',
|
|
1471
|
+
'f.defaultSize': '默认图像尺寸',
|
|
1472
|
+
'f.defaultSizeHint': '未指定尺寸时的默认生成分辨率。',
|
|
1473
|
+
'f.defaultFormat': '输出格式',
|
|
1474
|
+
'f.defaultFormatHint': '生成图像的默认文件格式(png 或 webp)。',
|
|
1475
|
+
'f.deliverAs': '输出投递方式',
|
|
1476
|
+
'f.deliverAsHint': 'link 模式仅返回链接;image 模式同时返回图像自身数据。',
|
|
1477
|
+
'f.outputDir': '存储目录',
|
|
1478
|
+
'f.outputDirHint': '已生成图像在会话工作空间内的保存目录。',
|
|
1479
|
+
'p.outputDir': 'generated/images',
|
|
1480
|
+
'f.historyLimit': '历史记录数量',
|
|
1481
|
+
'f.historyLimitHint': '保存在内存历史列表中的最大条目数。',
|
|
1482
|
+
'p.historyLimit': '50',
|
|
1483
|
+
|
|
1484
|
+
'f.model': 'FAL 模型 ID',
|
|
1485
|
+
'f.modelHint': '调用的 FAL 队列模型标识符。',
|
|
1486
|
+
'p.model': 'fal-ai/flux-2/klein/9b',
|
|
1487
|
+
'f.apiKeyEnv': 'FAL 凭证名称',
|
|
1488
|
+
'f.apiKeyEnvHint': '存储 FAL API 密钥的环境变量或凭证名称。',
|
|
1489
|
+
'p.apiKeyEnv': 'FAL_API_KEY',
|
|
1490
|
+
'f.baseURL': 'FAL 基础地址',
|
|
1491
|
+
'f.baseURLHint': 'FAL 队列服务基础 URL。',
|
|
1492
|
+
'p.baseURL': 'https://queue.fal.run',
|
|
1493
|
+
'f.pollIntervalMs': '轮询间隔(毫秒)',
|
|
1494
|
+
'f.pollIntervalMsHint': '异步队列状态检查的时间间隔。',
|
|
1495
|
+
'p.pollIntervalMs': '2000',
|
|
1496
|
+
'f.timeoutMs': '超时上限(毫秒)',
|
|
1497
|
+
'f.timeoutMsHint': '单次生成作业的总执行时限。',
|
|
1498
|
+
'p.timeoutMs': '180000',
|
|
1499
|
+
|
|
1500
|
+
'f.customBaseURL': 'Custom API 地址',
|
|
1501
|
+
'f.customBaseURLHint': '兼容 OpenAI 图像接口的基础地址。',
|
|
1502
|
+
'p.customBaseURL': 'https://api.openai.com/v1',
|
|
1503
|
+
'f.customModel': 'Custom 模型 ID',
|
|
1504
|
+
'f.customModelHint': '自定义接口的模型标识符。',
|
|
1505
|
+
'p.customModel': 'gpt-image-1',
|
|
1506
|
+
'f.customKeyEnv': 'Custom 凭证名称',
|
|
1507
|
+
'f.customKeyEnvHint': '存储自定义接口 API 密钥的凭证名称。',
|
|
1508
|
+
'p.customKeyEnv': 'OPENAI_API_KEY',
|
|
1509
|
+
'f.customSize': 'Custom 固定尺寸',
|
|
1510
|
+
'f.customSizeHint': '发送至自定义接口的指定分辨率(如 1024x1024)。',
|
|
1511
|
+
'p.customSize': '1024x1024',
|
|
1512
|
+
|
|
1513
|
+
'f.replicateModel': 'Replicate 模型 ID',
|
|
1514
|
+
'f.replicateModelHint': 'Replicate 平台上的目标模型。',
|
|
1515
|
+
'p.replicateModel': 'black-forest-labs/flux-schnell',
|
|
1516
|
+
'f.replicateKeyEnv': 'Replicate 凭证名称',
|
|
1517
|
+
'f.replicateKeyEnvHint': '存储 Replicate API Token 的凭证名称。',
|
|
1518
|
+
'p.replicateKeyEnv': 'REPLICATE_API_TOKEN',
|
|
1519
|
+
|
|
1520
|
+
'f.seedreamModel': 'SeaDream 模型 ID',
|
|
1521
|
+
'f.seedreamModelHint': '字节跳动 SeaDream 图像模型标识符。',
|
|
1522
|
+
'p.seedreamModel': 'seedream-4.0',
|
|
1523
|
+
'f.seedreamKeyEnv': 'SeaDream 凭证名称',
|
|
1524
|
+
'f.seedreamKeyEnvHint': '存储 SeaDream 接口密钥的凭证名称。',
|
|
1525
|
+
'p.seedreamKeyEnv': 'SEEDREAM_API_KEY',
|
|
1526
|
+
'f.seedreamBaseURL': 'SeaDream 接口地址',
|
|
1527
|
+
'f.seedreamBaseURLHint': 'SeaDream API 基础地址(火山引擎 Ark)。',
|
|
1528
|
+
'p.seedreamBaseURL': 'https://api.bytedanceapi.com/v1',
|
|
1529
|
+
|
|
1530
|
+
'f.geminiModel': 'Gemini 模型 ID',
|
|
1531
|
+
'f.geminiModelHint': 'Google Gemini 图像生成模型标识符。',
|
|
1532
|
+
'p.geminiModel': 'gemini-2.0-flash-exp-image-generation',
|
|
1533
|
+
'f.geminiKeyEnv': 'Gemini 凭证名称',
|
|
1534
|
+
'f.geminiKeyEnvHint': '存储 Google Gemini 密钥的凭证名称。',
|
|
1535
|
+
'p.geminiKeyEnv': 'GEMINI_API_KEY',
|
|
1536
|
+
|
|
1537
|
+
'f.localKind': '本地引擎架构',
|
|
1538
|
+
'f.localKindHint': '在 ComfyUI API 或 Automatic1111 WebUI 架构间选择。',
|
|
1539
|
+
'f.localBaseURL': '本地服务地址',
|
|
1540
|
+
'f.localBaseURLHint': '服务网络地址(如 http://127.0.0.1:8188)。',
|
|
1541
|
+
'p.localBaseURL': 'http://127.0.0.1:8188',
|
|
1542
|
+
'f.localModel': '本地模型 / Checkpoint',
|
|
1543
|
+
'f.localModelHint': '本地模型文件名或 ComfyUI 工作流名称。',
|
|
1544
|
+
'p.localModel': 'v1-5-pruned-emaonly.safetensors',
|
|
1545
|
+
'f.localSteps': '采样步数',
|
|
1546
|
+
'f.localStepsHint': '去噪采样的计算步数。',
|
|
1547
|
+
'p.localSteps': '20',
|
|
1548
|
+
'f.localCfg': 'CFG Scale',
|
|
1549
|
+
'f.localCfgHint': '无分类器引导系数。',
|
|
1550
|
+
'p.localCfg': '7',
|
|
1551
|
+
'f.localComfyUrl': '本地 ComfyUI 地址 (Legacy)',
|
|
1552
|
+
'f.localComfyUrlHint': '直接指向 ComfyUI 的服务地址。',
|
|
1553
|
+
'p.localComfyUrl': 'http://127.0.0.1:8188',
|
|
1554
|
+
'f.localA1111Url': '本地 A1111 地址 (Legacy)',
|
|
1555
|
+
'f.localA1111UrlHint': '直接指向 Automatic1111 的服务地址。',
|
|
1556
|
+
'p.localA1111Url': 'http://127.0.0.1:7860',
|
|
1557
|
+
|
|
1558
|
+
'f.subscriptionQuality': '订阅质量',
|
|
1559
|
+
'f.subscriptionQualityHint': 'Codex 或 Grok 订阅通道的质量档位。',
|
|
1560
|
+
|
|
1561
|
+
'f.autoEnhancePrompt': '智能自动增强提示词',
|
|
1562
|
+
'f.autoEnhancePromptHint': '自动为提示词补充最佳摄影、光影与构图词汇。',
|
|
1563
|
+
'f.defaultStylePreset': '默认风格预设',
|
|
1564
|
+
'f.defaultStylePresetHint': '生成图像时应用的默认艺术或摄影风格(如 cinematic, anime 等)。',
|
|
1565
|
+
'p.defaultStylePreset': 'none',
|
|
1566
|
+
|
|
1567
|
+
'f.enhancePrompt': 'LLM 提示词扩展',
|
|
1568
|
+
'f.enhancePromptHint': '在生成前由会话模型自动丰富短提示词细节。',
|
|
1569
|
+
'f.enableLlmEnhancer': '启用 LLM 提示词增强 (Legacy)',
|
|
1570
|
+
'f.enableLlmEnhancerHint': '提示词扩展的历史遗留开关。',
|
|
1571
|
+
'f.enhanceModel': '增强模型',
|
|
1572
|
+
'f.enhanceModelHint': '用于扩展提示词的独立大语言模型(留空使用会话模型)。',
|
|
1573
|
+
'p.enhanceModel': '留空表示使用当前会话模型',
|
|
1574
|
+
'f.enhanceBelowChars': '扩展长度阈值',
|
|
1575
|
+
'f.enhanceBelowCharsHint': '长于此字数的提示词将直接生成,不予额外扩展。',
|
|
1576
|
+
'p.enhanceBelowChars': '200',
|
|
1577
|
+
'f.stylePreset': '艺术风格后缀',
|
|
1578
|
+
'f.stylePresetHint': '自动追加到所有生成提示词末尾的风格后缀。',
|
|
1579
|
+
|
|
1580
|
+
'f.qualityGate': '静默质量门禁',
|
|
1581
|
+
'f.qualityGateHint': '自动检测破损或空白输出,并在后台静默重新生成。',
|
|
1582
|
+
'f.dailyBudgetUsd': '每日开销预算 ($ USD)',
|
|
1583
|
+
'f.dailyBudgetUsdHint': '所有图像工具每 24 小时的支出预算上限(0 为不限制)。',
|
|
1584
|
+
'p.dailyBudgetUsd': '0.00',
|
|
1585
|
+
'f.loopGuardLimit': '防死循环守卫 (Loop Guard)',
|
|
1586
|
+
'f.loopGuardLimitHint': '无需用户交互的连续生成操作上限(0 为禁用)。',
|
|
1587
|
+
'p.loopGuardLimit': '3',
|
|
1588
|
+
|
|
1589
|
+
'f.diskCache': '按内容哈希的磁盘缓存',
|
|
1590
|
+
'f.diskCacheHint': '重复生成完全相同的请求时实现毫秒级瞬间返回与零支出。',
|
|
1591
|
+
'f.cacheBySeed': '按 Seed 与提示词命中缓存',
|
|
1592
|
+
'f.cacheBySeedHint': '当 Seed 和提示词均完全一致时直接命中缓存。',
|
|
1593
|
+
'f.cacheByPrompt': '按提示词命中缓存',
|
|
1594
|
+
'f.cacheByPromptHint': '当提示词文本一致时直接命中缓存。',
|
|
1595
|
+
'f.pruneDays': '文件保留天数',
|
|
1596
|
+
'f.pruneDaysHint': '自动清理超过该天数的已生成图像(0 表示永久保留)。',
|
|
1597
|
+
'p.pruneDays': '0',
|
|
1598
|
+
'f.cacheTtlDays': '缓存生命周期 (Legacy)',
|
|
1599
|
+
'f.cacheTtlDaysHint': '已废弃的缓存保留时长参数。',
|
|
1600
|
+
'p.cacheTtlDays': '0',
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1040
1603
|
const ru = {
|
|
1041
1604
|
'settings.navLabel': 'Image Studio',
|
|
1042
1605
|
'settings.title': 'Генерация изображений',
|
|
@@ -1227,6 +1790,7 @@ window.__ModuleLoader__.load({
|
|
|
1227
1790
|
function apply(ctx) {
|
|
1228
1791
|
if (ctx.locale && typeof ctx.locale.define === 'function') {
|
|
1229
1792
|
ctx.locale.define('en', NS, en)
|
|
1793
|
+
ctx.locale.define('zh', NS, zh)
|
|
1230
1794
|
ctx.locale.define('ru', NS, ru)
|
|
1231
1795
|
}
|
|
1232
1796
|
|
|
@@ -1274,6 +1838,7 @@ window.__ModuleLoader__.load({
|
|
|
1274
1838
|
'remove_background',
|
|
1275
1839
|
'upscale_image',
|
|
1276
1840
|
'vectorize_image',
|
|
1841
|
+
'assemble_image_grid',
|
|
1277
1842
|
]
|
|
1278
1843
|
|
|
1279
1844
|
for (const toolName of toolviewTools) {
|
|
@@ -1290,6 +1855,95 @@ window.__ModuleLoader__.load({
|
|
|
1290
1855
|
)
|
|
1291
1856
|
}
|
|
1292
1857
|
|
|
1858
|
+
|
|
1859
|
+
// Native Sidebar Right Pane Tab & BetterSidebar
|
|
1860
|
+
if (typeof ctx.inject === 'function') {
|
|
1861
|
+
try {
|
|
1862
|
+
ctx.inject(['sidebarRightTabs'], (sctx) => {
|
|
1863
|
+
const tabs = sctx && sctx.sidebarRightTabs
|
|
1864
|
+
if (!tabs || typeof tabs.register !== 'function') return
|
|
1865
|
+
try {
|
|
1866
|
+
const def = {
|
|
1867
|
+
id: '@goodandready/dsh-image-gen:gallery',
|
|
1868
|
+
kind: 'image-gallery',
|
|
1869
|
+
priority: 'extension',
|
|
1870
|
+
title: () => 'Gallery',
|
|
1871
|
+
guide: [
|
|
1872
|
+
{
|
|
1873
|
+
order: 45,
|
|
1874
|
+
title: () => 'Image Studio Gallery',
|
|
1875
|
+
description: () => 'Browse and inspect recent image generations',
|
|
1876
|
+
icon: () => react.createElement('span', null, '🖼️'),
|
|
1877
|
+
},
|
|
1878
|
+
],
|
|
1879
|
+
}
|
|
1880
|
+
if (typeof sctx.effect === 'function') {
|
|
1881
|
+
sctx.effect(() => tabs.register(def))
|
|
1882
|
+
} else {
|
|
1883
|
+
tabs.register(def)
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
if (ctx.slots) {
|
|
1887
|
+
const registerPaneTab = () => {
|
|
1888
|
+
try {
|
|
1889
|
+
return ctx.slots.register(
|
|
1890
|
+
{
|
|
1891
|
+
name: 'sidebar.right.pane.tab',
|
|
1892
|
+
key: '@goodandready/dsh-image-gen:gallery',
|
|
1893
|
+
locale: NS,
|
|
1894
|
+
inject: () => ({ ctx }),
|
|
1895
|
+
},
|
|
1896
|
+
(props) => react.createElement(ErrorBoundary, null, react.createElement(GalleryView, { ...props, ctx }))
|
|
1897
|
+
)
|
|
1898
|
+
} catch (e) {
|
|
1899
|
+
console.warn('[dsh-image-gen] native sidebar pane tab register failed', e)
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
if (typeof ctx.slots.inject === 'function') {
|
|
1903
|
+
ctx.slots.inject('sidebar.right.pane.tab', registerPaneTab)
|
|
1904
|
+
} else {
|
|
1905
|
+
registerPaneTab()
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
} catch (e) {
|
|
1909
|
+
console.warn('[dsh-image-gen] native sidebar registration failed', e)
|
|
1910
|
+
}
|
|
1911
|
+
})
|
|
1912
|
+
} catch (e) {}
|
|
1913
|
+
|
|
1914
|
+
try {
|
|
1915
|
+
ctx.inject(['betterSidebar'], (sctx) => {
|
|
1916
|
+
const svc = sctx && sctx.betterSidebar
|
|
1917
|
+
if (!svc || typeof svc.registerTab !== 'function') return
|
|
1918
|
+
try {
|
|
1919
|
+
svc.registerTab({
|
|
1920
|
+
id: 'dsh-image-gen:gallery',
|
|
1921
|
+
title: () => 'Gallery',
|
|
1922
|
+
icon: () => react.createElement('span', null, '🖼️'),
|
|
1923
|
+
order: 45,
|
|
1924
|
+
component: ({ scope }) => react.createElement(ErrorBoundary, null, react.createElement(GalleryView, { ctx, scope })),
|
|
1925
|
+
})
|
|
1926
|
+
} catch (e) {
|
|
1927
|
+
console.warn('[dsh-image-gen] betterSidebar registerTab failed', e)
|
|
1928
|
+
}
|
|
1929
|
+
})
|
|
1930
|
+
} catch (e) {}
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1933
|
+
// Conversation header utilities chip (quick-access gallery button in chat header)
|
|
1934
|
+
registerSlotWhenReady('conversation.session.header.utilities', () =>
|
|
1935
|
+
ctx.slots.register(
|
|
1936
|
+
{
|
|
1937
|
+
name: 'conversation.session.header.utilities',
|
|
1938
|
+
id: 'dsh-image-gen-gallery-chip',
|
|
1939
|
+
order: 8,
|
|
1940
|
+
locale: NS,
|
|
1941
|
+
inject: () => ({ ctx }),
|
|
1942
|
+
},
|
|
1943
|
+
(props) => react.createElement(GalleryHeaderChip, { ...props, ctx })
|
|
1944
|
+
)
|
|
1945
|
+
)
|
|
1946
|
+
|
|
1293
1947
|
// Settings card item
|
|
1294
1948
|
registerSlotWhenReady('settings.plugin.item', () =>
|
|
1295
1949
|
ctx.slots.register(
|
|
@@ -1308,4 +1962,4 @@ window.__ModuleLoader__.load({
|
|
|
1308
1962
|
exports.inject = inject
|
|
1309
1963
|
return module.exports
|
|
1310
1964
|
},
|
|
1311
|
-
})
|
|
1965
|
+
})
|