feat: Introduce a new sidebar for settings and inspector, and add glass reset options and info panel positioning improvements.
This commit is contained in:
@@ -0,0 +1,664 @@
|
||||
/**
|
||||
* ComfyUI MagnifyGlass - Info Panel Styles
|
||||
*
|
||||
* Complete CSS styles for the info panel component.
|
||||
*/
|
||||
|
||||
/* SVG Icon Styling */
|
||||
.magnify-info-panel svg,
|
||||
.floating-controls svg {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: middle;
|
||||
fill: none;
|
||||
stroke: currentColor;
|
||||
pointer-events: none;
|
||||
/* Allow clicks to pass through to button */
|
||||
}
|
||||
|
||||
.header-icon svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.section-icon svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.control-btn svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.expand-icon svg {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.empty-state-icon svg {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
stroke-width: 1.5;
|
||||
}
|
||||
|
||||
.magnify-info-panel {
|
||||
border-radius: 12px;
|
||||
backdrop-filter: blur(20px);
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(255, 255, 255, 0.1);
|
||||
overflow: hidden;
|
||||
font-size: 16px;
|
||||
line-height: 1.6;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.magnify-info-panel * {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.magnify-info-panel.theme-dark {
|
||||
background: linear-gradient(135deg, rgba(0, 0, 0, 0.96), rgba(10, 10, 15, 0.96));
|
||||
color: var(--info-panel-text-color, #e0e0e0);
|
||||
border: 1px solid rgba(100, 100, 120, 0.4);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3), 0 2px 16px rgba(0, 0, 0, 0.2);
|
||||
backdrop-filter: blur(12px);
|
||||
}
|
||||
|
||||
.magnify-info-panel.theme-light {
|
||||
background: linear-gradient(135deg, rgba(250, 250, 255, 0.96), rgba(240, 240, 250, 0.96));
|
||||
color: var(--info-panel-text-color, #2a2a2a);
|
||||
border: 1px solid rgba(200, 200, 220, 0.5);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1), 0 2px 16px rgba(0, 0, 0, 0.05);
|
||||
backdrop-filter: blur(12px);
|
||||
}
|
||||
|
||||
/* Solarized Theme - warm, muted tones */
|
||||
.magnify-info-panel.theme-solarized {
|
||||
background: linear-gradient(135deg, rgba(0, 43, 54, 0.96), rgba(7, 54, 66, 0.96));
|
||||
color: var(--info-panel-text-color, #839496);
|
||||
border: 1px solid rgba(88, 110, 117, 0.5);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3), 0 2px 16px rgba(0, 0, 0, 0.2);
|
||||
backdrop-filter: blur(12px);
|
||||
--info-panel-accent-color: #268bd2;
|
||||
}
|
||||
|
||||
/* Arc Theme - modern blue-gray */
|
||||
.magnify-info-panel.theme-arc {
|
||||
background: linear-gradient(135deg, rgba(56, 60, 74, 0.96), rgba(64, 69, 82, 0.96));
|
||||
color: var(--info-panel-text-color, #d3dae3);
|
||||
border: 1px solid rgba(90, 100, 120, 0.5);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.25), 0 2px 16px rgba(0, 0, 0, 0.15);
|
||||
backdrop-filter: blur(12px);
|
||||
--info-panel-accent-color: #5294e2;
|
||||
}
|
||||
|
||||
/* Nord Theme - cool arctic colors */
|
||||
.magnify-info-panel.theme-nord {
|
||||
background: linear-gradient(135deg, rgba(46, 52, 64, 0.96), rgba(59, 66, 82, 0.96));
|
||||
color: var(--info-panel-text-color, #d8dee9);
|
||||
border: 1px solid rgba(76, 86, 106, 0.5);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.25), 0 2px 16px rgba(0, 0, 0, 0.15);
|
||||
backdrop-filter: blur(12px);
|
||||
--info-panel-accent-color: #88c0d0;
|
||||
}
|
||||
|
||||
/* GitHub Theme - clean, professional */
|
||||
.magnify-info-panel.theme-github {
|
||||
background: linear-gradient(135deg, rgba(255, 255, 255, 0.96), rgba(246, 248, 250, 0.96));
|
||||
color: var(--info-panel-text-color, #24292e);
|
||||
border: 1px solid rgba(225, 228, 232, 0.8);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1), 0 2px 16px rgba(0, 0, 0, 0.05);
|
||||
backdrop-filter: blur(12px);
|
||||
--info-panel-accent-color: #0366d6;
|
||||
}
|
||||
|
||||
.magnify-info-panel.visible {
|
||||
transform: translateY(0) !important;
|
||||
}
|
||||
|
||||
.magnify-info-panel.panel-pinned {
|
||||
cursor: grab;
|
||||
box-shadow: 0 0 0 2px rgba(255, 215, 0, 0.5), 0 20px 40px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.magnify-info-panel.panel-locked {
|
||||
cursor: default !important;
|
||||
box-shadow: 0 0 0 2px rgba(255, 69, 0, 0.5), 0 20px 40px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.magnify-info-panel.panel-locked::after {
|
||||
content: '📌';
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
font-size: 14px;
|
||||
opacity: 0.7;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.magnify-info-panel.panel-pinned.panel-dragging {
|
||||
cursor: grabbing;
|
||||
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.5), 0 0 0 2px rgba(255, 215, 0, 0.7);
|
||||
transform: scale(1.02) !important;
|
||||
z-index: 100001 !important;
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
.magnify-info-panel.panel-pinned::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(45deg, rgba(255, 215, 0, 0.05) 0%, transparent 100%);
|
||||
pointer-events: none;
|
||||
border-radius: inherit;
|
||||
}
|
||||
|
||||
.panel-dragging * {
|
||||
pointer-events: none !important;
|
||||
user-select: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
.magnify-info-panel.panel-minimized .panel-content {
|
||||
max-height: 0 !important;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.floating-controls {
|
||||
position: fixed;
|
||||
display: none;
|
||||
/* Hidden initially - JS will show when positioned */
|
||||
padding: 4px;
|
||||
background: rgba(40, 40, 50, 0.95);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 4px;
|
||||
opacity: 1;
|
||||
z-index: 100000;
|
||||
backdrop-filter: blur(8px);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.floating-controls.vertical-layout {
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.floating-controls.horizontal-layout {
|
||||
flex-direction: row;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
background: rgba(60, 60, 70, 0.8);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
color: #ddd;
|
||||
border-radius: 3px;
|
||||
transition: background-color 0.15s ease;
|
||||
}
|
||||
|
||||
.control-btn:hover {
|
||||
background: rgba(80, 80, 90, 0.9);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.control-btn:active {
|
||||
background: rgba(100, 100, 110, 0.9);
|
||||
}
|
||||
|
||||
.control-btn:disabled {
|
||||
background: rgba(40, 40, 50, 0.5);
|
||||
color: #666;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.control-btn.active {
|
||||
background: rgba(255, 200, 50, 0.3);
|
||||
border-color: rgba(255, 200, 50, 0.6);
|
||||
color: #ffcc33;
|
||||
}
|
||||
|
||||
.control-btn.lock-btn.active {
|
||||
background: rgba(255, 69, 0, 0.3);
|
||||
border-color: rgba(255, 69, 0, 0.6);
|
||||
color: #ff4500;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 14px 18px;
|
||||
background: linear-gradient(135deg, rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.05));
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
user-select: none;
|
||||
cursor: grab;
|
||||
transition: background 0.15s ease;
|
||||
}
|
||||
|
||||
.panel-header:hover {
|
||||
background: linear-gradient(135deg, rgba(0, 0, 0, 0.15), rgba(255, 255, 255, 0.08));
|
||||
}
|
||||
|
||||
.panel-header:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.header-icon {
|
||||
font-size: 18px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-weight: 600;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.header-subtitle {
|
||||
font-size: 14px;
|
||||
opacity: 0.7;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.header-controls {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.minimize-btn {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
color: inherit;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.minimize-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.minimize-btn:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.panel-content {
|
||||
padding: 0;
|
||||
max-height: calc(100% - 60px);
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.panel-content::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.panel-content::-webkit-scrollbar-track {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.panel-content::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.info-section {
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
margin-bottom: 8px;
|
||||
border-radius: 10px;
|
||||
overflow: visible;
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.info-section:hover {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 16px 20px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
font-weight: 500;
|
||||
border-radius: 8px;
|
||||
margin: 3px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.section-header::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.05) 50%, transparent 100%);
|
||||
transform: translateX(-100%);
|
||||
transition: transform 0.6s ease;
|
||||
}
|
||||
|
||||
.section-header:hover::before {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
.section-header:hover {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
transform: translateX(2px);
|
||||
box-shadow: inset 2px 0 0 rgba(116, 185, 255, 0.5);
|
||||
}
|
||||
|
||||
.section-header:active {
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
transform: translateX(1px);
|
||||
}
|
||||
|
||||
.section-header.expanded {
|
||||
background: rgba(116, 185, 255, 0.1);
|
||||
border-left: 2px solid var(--info-panel-accent-color, #74b9ff);
|
||||
}
|
||||
|
||||
.info-section[data-section="node"] {
|
||||
background: linear-gradient(135deg, rgba(116, 185, 255, 0.05), rgba(160, 212, 104, 0.05));
|
||||
border: 1px solid rgba(116, 185, 255, 0.2);
|
||||
border-radius: 8px;
|
||||
margin: 6px;
|
||||
}
|
||||
|
||||
.info-section[data-section="node"] .section-header {
|
||||
background: linear-gradient(135deg, rgba(116, 185, 255, 0.15), rgba(160, 212, 104, 0.15));
|
||||
border-radius: 8px 8px 0 0;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.info-section[data-section="node"] .section-header:hover {
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.info-section[data-section="node"] .section-title {
|
||||
color: var(--info-panel-accent-color, #74b9ff);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.info-section[data-section="inspector"] {
|
||||
background: linear-gradient(135deg, rgba(100, 150, 255, 0.05), rgba(74, 144, 226, 0.05));
|
||||
border: 1px solid rgba(100, 150, 255, 0.2);
|
||||
border-radius: 8px;
|
||||
margin: 6px;
|
||||
}
|
||||
|
||||
.info-section[data-section="inspector"] .section-header {
|
||||
background: linear-gradient(135deg, rgba(100, 150, 255, 0.15), rgba(74, 144, 226, 0.15));
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
|
||||
.info-section[data-section="inspector"] .section-title {
|
||||
color: #6496ff;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.info-section[data-section="media"] {
|
||||
background: linear-gradient(135deg, rgba(253, 203, 110, 0.05), rgba(255, 215, 0, 0.05));
|
||||
border: 1px solid rgba(253, 203, 110, 0.2);
|
||||
border-radius: 8px;
|
||||
margin: 6px;
|
||||
}
|
||||
|
||||
.info-section[data-section="media"] .section-header {
|
||||
background: linear-gradient(135deg, rgba(253, 203, 110, 0.15), rgba(255, 215, 0, 0.15));
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
|
||||
.info-section[data-section="media"] .section-title {
|
||||
color: var(--info-panel-accent-color, #fdcb6e);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.section-icon {
|
||||
font-size: 18px;
|
||||
width: 22px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
flex: 1;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.3px;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.section-badge {
|
||||
background: rgba(100, 150, 255, 0.2);
|
||||
color: #8bb3ff;
|
||||
padding: 3px 10px;
|
||||
border-radius: 10px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.expand-icon {
|
||||
font-size: 14px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.section-header:hover .expand-icon {
|
||||
color: var(--info-panel-accent-color, #74b9ff);
|
||||
background: rgba(116, 185, 255, 0.15);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.section-header.expanded .expand-icon {
|
||||
transform: rotate(90deg) scale(1.1);
|
||||
color: var(--info-panel-accent-color, #74b9ff);
|
||||
background: rgba(116, 185, 255, 0.2);
|
||||
}
|
||||
|
||||
.section-content {
|
||||
max-height: 0;
|
||||
overflow: visible;
|
||||
transition: max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s ease;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.section-content.expanded {
|
||||
max-height: 2000px;
|
||||
opacity: 1;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.section-body {
|
||||
padding: 0 18px 16px 24px;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||
transition: all 0.2s ease;
|
||||
border-radius: 4px;
|
||||
margin: 0 -8px;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.info-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.info-row:hover {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border-bottom-color: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-weight: 500;
|
||||
opacity: 0.85;
|
||||
font-size: 15px;
|
||||
min-width: 80px;
|
||||
padding-right: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
font-size: 14px;
|
||||
color: var(--info-panel-accent-color, #a0d468);
|
||||
background: rgba(160, 212, 104, 0.12);
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
max-width: 100%;
|
||||
word-wrap: break-word;
|
||||
word-break: break-word;
|
||||
hyphens: auto;
|
||||
line-height: 1.4;
|
||||
text-align: left;
|
||||
white-space: pre-wrap;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.info-value:hover {
|
||||
background: rgba(160, 212, 104, 0.18);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.info-value.long-text {
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
max-width: 100%;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 50px 25px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.empty-state-icon {
|
||||
font-size: 36px;
|
||||
margin-bottom: 15px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.empty-state-text {
|
||||
font-size: 16px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Copy to clipboard functionality */
|
||||
.info-value[title]:hover::after {
|
||||
content: '📋';
|
||||
position: absolute;
|
||||
right: -20px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
font-size: 12px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* Smooth loading states */
|
||||
.section-body.loading {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.section-body.loading::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid transparent;
|
||||
border-top: 2px solid var(--info-panel-accent-color, #74b9ff);
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: translate(-50%, -50%) rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Enhanced scrollbar for better UX */
|
||||
.section-body::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
.section-body::-webkit-scrollbar-track {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.section-body::-webkit-scrollbar-thumb {
|
||||
background: var(--info-panel-accent-color, #74b9ff);
|
||||
border-radius: 2px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.section-body::-webkit-scrollbar-thumb:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
.pulse {
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
+352
@@ -0,0 +1,352 @@
|
||||
/**
|
||||
* ComfyUI MagnifyGlass - Sidebar CSS
|
||||
*
|
||||
* Styles for the sidebar settings and inspector panels.
|
||||
*/
|
||||
|
||||
.magnify-sidebar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
font-size: 13px;
|
||||
color: var(--fg-color, #ddd);
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.magnify-sidebar-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid var(--border-color, #333);
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.magnify-sidebar-header svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.magnify-sidebar-header h2 {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.magnify-sidebar-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
/* Section styling */
|
||||
.magnify-sidebar-section {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.magnify-sidebar-section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 16px;
|
||||
cursor: pointer;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border-bottom: 1px solid var(--border-color, #333);
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.magnify-sidebar-section-header:hover {
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
.magnify-sidebar-section-header svg {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.magnify-sidebar-section-header.collapsed svg {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.magnify-sidebar-section-header span {
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.magnify-sidebar-section-body {
|
||||
padding: 12px 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.magnify-sidebar-section-body.collapsed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Control row */
|
||||
.magnify-control-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.magnify-control-row label {
|
||||
font-size: 11px;
|
||||
color: var(--descrip-text, #999);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.magnify-control-row .control-value {
|
||||
font-size: 11px;
|
||||
color: var(--fg-color, #ddd);
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.magnify-control-label-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Slider styling */
|
||||
.magnify-slider {
|
||||
width: 100%;
|
||||
height: 6px;
|
||||
border-radius: 3px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
appearance: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.magnify-slider::-webkit-slider-thumb {
|
||||
appearance: none;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 50%;
|
||||
background: var(--p-primary-color, #3b82f6);
|
||||
cursor: pointer;
|
||||
transition: transform 0.1s;
|
||||
}
|
||||
|
||||
.magnify-slider::-webkit-slider-thumb:hover {
|
||||
transform: scale(1.15);
|
||||
}
|
||||
|
||||
.magnify-slider::-moz-range-thumb {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 50%;
|
||||
background: var(--p-primary-color, #3b82f6);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Toggle styling */
|
||||
.magnify-toggle-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
.magnify-toggle-row label {
|
||||
font-size: 12px;
|
||||
color: var(--fg-color, #ddd);
|
||||
}
|
||||
|
||||
.magnify-toggle {
|
||||
position: relative;
|
||||
width: 36px;
|
||||
height: 20px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.magnify-toggle.active {
|
||||
background: var(--p-primary-color, #3b82f6);
|
||||
}
|
||||
|
||||
.magnify-toggle::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: white;
|
||||
border-radius: 50%;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.magnify-toggle.active::after {
|
||||
transform: translateX(16px);
|
||||
}
|
||||
|
||||
/* Select styling */
|
||||
.magnify-select {
|
||||
width: 100%;
|
||||
padding: 6px 10px;
|
||||
border: 1px solid var(--border-color, #444);
|
||||
border-radius: 4px;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
color: var(--fg-color, #ddd);
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
appearance: none;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23999' d='M6 8L2 4h8z'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 8px center;
|
||||
}
|
||||
|
||||
.magnify-select:focus {
|
||||
outline: none;
|
||||
border-color: var(--p-primary-color, #3b82f6);
|
||||
}
|
||||
|
||||
/* Inspector styles */
|
||||
.magnify-inspector-section {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.magnify-inspector-group {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.magnify-inspector-group:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.magnify-inspector-group-title {
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
color: var(--descrip-text, #888);
|
||||
margin-bottom: 8px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.magnify-inspector-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 4px 0;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.magnify-inspector-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.magnify-inspector-label {
|
||||
color: var(--descrip-text, #999);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.magnify-inspector-value {
|
||||
color: var(--fg-color, #ddd);
|
||||
font-size: 12px;
|
||||
font-family: monospace;
|
||||
text-align: right;
|
||||
max-width: 60%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.magnify-inspector-empty {
|
||||
color: var(--descrip-text, #666);
|
||||
font-style: italic;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
/* Node badge */
|
||||
.magnify-node-badge {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
background: var(--p-primary-color, #3b82f6);
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/* Button styling */
|
||||
.magnify-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
padding: 8px 12px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
background: var(--p-primary-color, #3b82f6);
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s, transform 0.1s;
|
||||
}
|
||||
|
||||
.magnify-btn:hover {
|
||||
background: var(--p-primary-hover-color, #2563eb);
|
||||
}
|
||||
|
||||
.magnify-btn:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.magnify-btn-secondary {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.magnify-btn-secondary:hover {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
/* Color picker styling */
|
||||
.magnify-color-row {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.magnify-color-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.magnify-color-input {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
padding: 0;
|
||||
border: 1px solid var(--border-color, #444);
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.magnify-color-input::-webkit-color-swatch-wrapper {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.magnify-color-input::-webkit-color-swatch {
|
||||
border-radius: 2px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.magnify-color-preview {
|
||||
font-size: 11px;
|
||||
font-family: monospace;
|
||||
color: var(--descrip-text, #999);
|
||||
}
|
||||
@@ -176,6 +176,8 @@ export class InfoPanel {
|
||||
|
||||
if (this.stateManager.state.settings["🔍MagnifyGlass.InfoPanelEnabled"] && this.magnifyGlass.state.active) {
|
||||
this.uiManager.show();
|
||||
// Ensure position is updated after potential dimension changes
|
||||
this.positionManager.positionPanel();
|
||||
} else {
|
||||
this.uiManager.hide();
|
||||
}
|
||||
|
||||
@@ -133,6 +133,13 @@ export class PositionManager {
|
||||
positionFloatingControls(controlsElement: HTMLElement | null): void {
|
||||
if (!controlsElement) return;
|
||||
|
||||
// Check if controls should be visible based on settings
|
||||
const settings = this.stateManager.state.settings;
|
||||
if (settings["🔍MagnifyGlass.ShowHoveringControls"] === false) {
|
||||
controlsElement.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
const isPanelVisible = this.stateManager.state.isPanelVisible;
|
||||
const magnifyGlass = window.comfyUIMagnifyGlass;
|
||||
let referenceRect: DOMRect | null = null;
|
||||
|
||||
@@ -59,7 +59,7 @@ export class StateManager {
|
||||
// Panel visibility and positioning
|
||||
isPanelVisible: false,
|
||||
wasPanelVisibleBeforeHide: false,
|
||||
isPanelMinimized: false,
|
||||
isPanelMinimized: true,
|
||||
isPanelPinned: false,
|
||||
isPanelLocked: false, // New lock state
|
||||
isAutoPinned: false, // Track if panel was auto-pinned by glass hide
|
||||
@@ -259,7 +259,7 @@ export class StateManager {
|
||||
target.textContent?.toLowerCase().includes('light') ||
|
||||
target.textContent?.toLowerCase().includes('dark') ||
|
||||
target.title?.toLowerCase().includes('theme') ||
|
||||
target.className?.toLowerCase().includes('theme');
|
||||
(typeof target.className === 'string' && target.className.toLowerCase().includes('theme'));
|
||||
|
||||
if (isThemeButton) {
|
||||
setTimeout(() => {
|
||||
|
||||
+36
-23
@@ -76,6 +76,10 @@ export class UIManager {
|
||||
this.elements.panel.appendChild(this.elements.content);
|
||||
|
||||
this.applyStyles();
|
||||
// Apply initial minimized state if set
|
||||
if (this.stateManager.state.isPanelMinimized) {
|
||||
this.elements.panel.classList.add('panel-minimized');
|
||||
}
|
||||
document.body.appendChild(this.elements.panel);
|
||||
|
||||
// Add click event delegation for panel elements
|
||||
@@ -114,7 +118,7 @@ export class UIManager {
|
||||
});
|
||||
|
||||
// Create floating controls after panel is in DOM
|
||||
if (this.stateManager.state.settings["🔍MagnifyGlass.ShowHoveringControls"]) {
|
||||
if (!this.elements.controls) {
|
||||
this.createFloatingControls();
|
||||
// Update control states immediately after creation
|
||||
this.updateControlStates();
|
||||
@@ -321,43 +325,52 @@ export class UIManager {
|
||||
|
||||
const settings = this.stateManager.state.settings;
|
||||
|
||||
// Apply custom text color via CSS variable if provided
|
||||
// Debug Log
|
||||
console.log(`[UIManager] applyStyles - MaxHeight: ${settings["🔍MagnifyGlass.InfoPanelMaxHeight"]}`);
|
||||
|
||||
// 1. Apply dimensions
|
||||
this.elements.panel.style.width = `${settings["🔍MagnifyGlass.InfoPanelWidth"]}px`;
|
||||
// Set both height and max-height so the panel always shows at this size
|
||||
const maxHeight = settings["🔍MagnifyGlass.InfoPanelMaxHeight"];
|
||||
this.elements.panel.style.height = `${maxHeight}px`;
|
||||
this.elements.panel.style.maxHeight = `${maxHeight}px`;
|
||||
|
||||
// 2. Apply static styles (only needed once or if overwritten, but safe to set)
|
||||
this.elements.panel.style.position = 'absolute';
|
||||
this.elements.panel.style.zIndex = '99999';
|
||||
this.elements.panel.style.transform = 'translateY(-10px)';
|
||||
this.elements.panel.style.pointerEvents = 'auto';
|
||||
this.elements.panel.style.userSelect = 'none';
|
||||
this.elements.panel.style.fontFamily = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif";
|
||||
|
||||
// 3. Apply transition
|
||||
this.elements.panel.style.transition = settings["🔍MagnifyGlass.InfoPanelAnimations"]
|
||||
? 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)'
|
||||
: 'none';
|
||||
|
||||
// 4. Apply Custom Colors (CSS Variables)
|
||||
const textColor = settings["🔍MagnifyGlass.InfoPanelTextColor"] as string | undefined;
|
||||
if (textColor && typeof textColor === 'string') {
|
||||
// Ensure color has # symbol
|
||||
const normalizedTextColor = textColor.startsWith('#') ? textColor : `#${textColor}`;
|
||||
this.elements.panel.style.setProperty('--info-panel-text-color', normalizedTextColor);
|
||||
}
|
||||
|
||||
// Apply custom accent color via CSS variable if provided
|
||||
const accentColor = settings["🔍MagnifyGlass.InfoPanelAccentColor"] as string | undefined;
|
||||
if (accentColor && typeof accentColor === 'string') {
|
||||
// Ensure color has # symbol
|
||||
const normalizedAccentColor = accentColor.startsWith('#') ? accentColor : `#${accentColor}`;
|
||||
this.elements.panel.style.setProperty('--info-panel-accent-color', normalizedAccentColor);
|
||||
}
|
||||
|
||||
// Apply opacity setting if panel is visible (convert percentage to decimal)
|
||||
// 5. Apply Opacity
|
||||
// Logic: If visible, use setting. If hidden, use 0.
|
||||
// We do NOT touch 'display' here to avoid hiding it if it's currently shown.
|
||||
// 'display' is managed by show() / hide() methods exclusively.
|
||||
if (this.stateManager.state.isPanelVisible) {
|
||||
const opacityPercent = Number(settings["🔍MagnifyGlass.InfoPanelOpacity"]) || 100;
|
||||
this.elements.panel.style.opacity = (opacityPercent / 100).toString();
|
||||
} else {
|
||||
this.elements.panel.style.opacity = '0';
|
||||
}
|
||||
|
||||
this.elements.panel.style.cssText = `
|
||||
position: absolute;
|
||||
width: ${settings["🔍MagnifyGlass.InfoPanelWidth"]}px;
|
||||
max-height: ${settings["🔍MagnifyGlass.InfoPanelMaxHeight"]}px;
|
||||
z-index: 99999;
|
||||
display: none;
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
transition: ${settings["🔍MagnifyGlass.InfoPanelAnimations"] ? 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)' : 'none'};
|
||||
pointer-events: auto;
|
||||
user-select: none;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
${textColor && typeof textColor === 'string' ? `--info-panel-text-color: ${textColor.startsWith('#') ? textColor : `#${textColor}`};` : ''}
|
||||
${accentColor && typeof accentColor === 'string' ? `--info-panel-accent-color: ${accentColor.startsWith('#') ? accentColor : `#${accentColor}`};` : ''}
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -456,7 +469,7 @@ export class UIManager {
|
||||
const settings = this.stateManager.state.settings;
|
||||
|
||||
// Inspector section
|
||||
if (settings["🔍MagnifyGlass.ShowInspectorTab"]) {
|
||||
if (settings["🔍MagnifyGlass.ShowInspectorTab"] && info.cursor && info.cursor.canvas) {
|
||||
const inspectorContent = [
|
||||
{ label: 'Cursor Canvas', value: `(${Math.round(info.cursor.canvas.x)}, ${Math.round(info.cursor.canvas.y)})` },
|
||||
{ label: 'Canvas Scale', value: `${(info.canvas.scale * 100).toFixed(1)}%` },
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import { isUserTyping } from '../shared/utils';
|
||||
import { Logger } from '../shared/logger';
|
||||
import { DEFAULT_PADDING } from '../shared/constants';
|
||||
import { DEFAULT_PADDING, DEFAULT_GLASS_Y_OFFSET } from '../shared/constants';
|
||||
import type { MagnifyGlass } from './MagnifyGlass';
|
||||
|
||||
/**
|
||||
@@ -212,7 +212,7 @@ export class EventHandler {
|
||||
if (!this.magnifyGlass.state.wasActivatedBefore && this.magnifyGlass.ui.glassDiv) {
|
||||
// First activation - position at top right of the window
|
||||
this.magnifyGlass.ui.glassDiv.style.left = `${window.innerWidth - glassSize - DEFAULT_PADDING}px`;
|
||||
this.magnifyGlass.ui.glassDiv.style.top = `${DEFAULT_PADDING}px`;
|
||||
this.magnifyGlass.ui.glassDiv.style.top = `${DEFAULT_GLASS_Y_OFFSET}px`;
|
||||
this.magnifyGlass.state.wasActivatedBefore = true;
|
||||
} else {
|
||||
this.magnifyGlass.ui.positionGlass(clientX, clientY);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import type { ComfyApp, ComfyNode } from '../types/comfyui';
|
||||
import { findLiteGraphCanvas, rectsOverlap, Rectangle } from '../shared/utils';
|
||||
import { DEFAULT_PADDING } from '../shared/constants';
|
||||
import { DEFAULT_PADDING, DEFAULT_GLASS_Y_OFFSET } from '../shared/constants';
|
||||
import { registerGlassSettings } from '../shared/settings';
|
||||
import { ConfigManager } from './ConfigManager';
|
||||
import { MagnifierState } from './MagnifierState';
|
||||
@@ -378,6 +378,9 @@ export class MagnifyGlass {
|
||||
resetOffsets(): void {
|
||||
this.config.resetOffsets();
|
||||
|
||||
// Also disable follow cursor
|
||||
this.config.followCursor = false;
|
||||
|
||||
if (this.state.active) {
|
||||
this.updateMagnifiedView();
|
||||
}
|
||||
@@ -386,7 +389,7 @@ export class MagnifyGlass {
|
||||
if (this.ui.glassDiv) {
|
||||
const glassSize = this.config.glassSize;
|
||||
this.ui.glassDiv.style.left = `${window.innerWidth - glassSize - DEFAULT_PADDING}px`;
|
||||
this.ui.glassDiv.style.top = `${DEFAULT_PADDING}px`;
|
||||
this.ui.glassDiv.style.top = `${DEFAULT_GLASS_Y_OFFSET}px`;
|
||||
this.state.wasActivatedBefore = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
import { MagnifyGlass } from './magnify-glass';
|
||||
import { Logger } from './shared/logger';
|
||||
import { initSidebar } from './sidebar';
|
||||
// @ts-ignore
|
||||
import { app } from "/scripts/app.js";
|
||||
|
||||
@@ -23,6 +24,9 @@ app.registerExtension({
|
||||
// Expose to window for other extensions (like info panel) to access
|
||||
window.comfyUIMagnifyGlass = magnifyGlass;
|
||||
|
||||
// Initialize sidebar
|
||||
initSidebar();
|
||||
|
||||
Logger.info('Magnify Glass extension initialized');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -52,6 +52,7 @@ export const TOGGLE_FOLLOW_KEYS = ["f", "g", "h", "j", "k"] as const;
|
||||
|
||||
// UI Constants
|
||||
export const DEFAULT_PADDING = 20;
|
||||
export const DEFAULT_GLASS_Y_OFFSET = 120; // Starting Y position offset (below top bar)
|
||||
export const DEFAULT_GLASS_SIZE = 300;
|
||||
export const DEFAULT_ZOOM_FACTOR = 300; // 3x as percentage
|
||||
export const DEFAULT_BORDER_WIDTH = 1;
|
||||
@@ -59,8 +60,8 @@ export const DEFAULT_BORDER_COLOR = "#6b7280";
|
||||
export const DEFAULT_OFFSET_STEP = 5;
|
||||
|
||||
// Panel Constants
|
||||
export const DEFAULT_PANEL_WIDTH = 320;
|
||||
export const DEFAULT_PANEL_MAX_HEIGHT = 1000;
|
||||
export const DEFAULT_PANEL_WIDTH = 300;
|
||||
export const DEFAULT_PANEL_MAX_HEIGHT = 300;
|
||||
export const DEFAULT_PANEL_OPACITY = 100;
|
||||
|
||||
// Z-Index hierarchy
|
||||
|
||||
@@ -284,4 +284,82 @@ export function registerGlassSettings(magnifyGlass: any): void {
|
||||
}
|
||||
}
|
||||
});
|
||||
app.ui.settings.addSetting({
|
||||
id: "🔍MagnifyGlass.Action.ResetPosition",
|
||||
name: "🔄 Magnify Glass: Reset Position",
|
||||
type: "boolean",
|
||||
defaultValue: false,
|
||||
tooltip: "Toggle to reset the glass position and disable follow cursor.",
|
||||
onChange: (value: any) => {
|
||||
if (value) {
|
||||
if (magnifyGlass?.resetOffsets) {
|
||||
magnifyGlass.resetOffsets();
|
||||
}
|
||||
// Reset the toggle back to false
|
||||
setTimeout(() => {
|
||||
try {
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.Action.ResetPosition", false);
|
||||
} catch (e) { /* ignore */ }
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
app.ui.settings.addSetting({
|
||||
id: "🔍MagnifyGlass.Action.ResetAll",
|
||||
name: "⚠️ Magnify Glass: Reset ALL Settings",
|
||||
type: "boolean",
|
||||
defaultValue: false,
|
||||
tooltip: "Toggle to reset ALL settings to defaults.",
|
||||
onChange: (value: any) => {
|
||||
if (value) {
|
||||
if (confirm("Reset ALL Magnify Glass settings?")) {
|
||||
// Glass Defaults
|
||||
app.ui.settings.setSettingValue('🔍MagnifyGlass.ZoomFactor', 300);
|
||||
app.ui.settings.setSettingValue('🔍MagnifyGlass.GlassSize', 250);
|
||||
app.ui.settings.setSettingValue('🔍MagnifyGlass.GlassShape', 'Rounded Square');
|
||||
app.ui.settings.setSettingValue('🔍MagnifyGlass.GlassPosition', 'Top-Right');
|
||||
app.ui.settings.setSettingValue('🔍MagnifyGlass.TextureFiltering', 'Linear');
|
||||
app.ui.settings.setSettingValue('🔍MagnifyGlass.BorderWidth', 2);
|
||||
app.ui.settings.setSettingValue('🔍MagnifyGlass.BorderColor', '#ffffff');
|
||||
app.ui.settings.setSettingValue('🔍MagnifyGlass.BorderEnabled', true);
|
||||
app.ui.settings.setSettingValue('🔍MagnifyGlass.FollowCursor', false);
|
||||
app.ui.settings.setSettingValue('🔍MagnifyGlass.AlwaysActiveMode', true);
|
||||
app.ui.settings.setSettingValue('🔍MagnifyGlass.ActivationKey', 'x');
|
||||
app.ui.settings.setSettingValue('🔍MagnifyGlass.ResetKey', 'o');
|
||||
app.ui.settings.setSettingValue('🔍MagnifyGlass.ToggleFollowCursorKey', 'h');
|
||||
app.ui.settings.setSettingValue('🔍MagnifyGlass.AltRequired', false);
|
||||
app.ui.settings.setSettingValue('🔍MagnifyGlass.OffsetStep', 10);
|
||||
app.ui.settings.setSettingValue('🔍MagnifyGlass.DebugMode', false);
|
||||
|
||||
// Panel Defaults
|
||||
app.ui.settings.setSettingValue('🔍MagnifyGlass.InfoPanelEnabled', true);
|
||||
app.ui.settings.setSettingValue('🔍MagnifyGlass.InfoPanelPosition', 'Bottom');
|
||||
app.ui.settings.setSettingValue('🔍MagnifyGlass.InfoPanelWidth', 300);
|
||||
app.ui.settings.setSettingValue('🔍MagnifyGlass.InfoPanelMaxHeight', 300);
|
||||
app.ui.settings.setSettingValue('🔍MagnifyGlass.InfoPanelOpacity', 95);
|
||||
app.ui.settings.setSettingValue('🔍MagnifyGlass.InfoPanelTextColor', '#6b7280');
|
||||
app.ui.settings.setSettingValue('🔍MagnifyGlass.InfoPanelAccentColor', '#3b82f6');
|
||||
app.ui.settings.setSettingValue('🔍MagnifyGlass.InfoPanelAnimations', false);
|
||||
app.ui.settings.setSettingValue('🔍MagnifyGlass.ShowHoveringControls', true);
|
||||
app.ui.settings.setSettingValue('🔍MagnifyGlass.ControlsPosition', 'bottom');
|
||||
app.ui.settings.setSettingValue('🔍MagnifyGlass.ToggleHotkey', 'i');
|
||||
app.ui.settings.setSettingValue('🔍MagnifyGlass.GlassPreviewToggleHotkey', 'g');
|
||||
app.ui.settings.setSettingValue('🔍MagnifyGlass.PinPanelHotkey', 'u');
|
||||
|
||||
// Also force position reset
|
||||
if (magnifyGlass?.resetOffsets) {
|
||||
magnifyGlass.resetOffsets();
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the toggle back to false
|
||||
setTimeout(() => {
|
||||
try {
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.Action.ResetAll", false);
|
||||
} catch (e) { /* ignore */ }
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
/**
|
||||
* ComfyUI MagnifyGlass - Sidebar Inspector Panel
|
||||
*
|
||||
* Real-time inspector view for the sidebar.
|
||||
*/
|
||||
|
||||
// @ts-ignore
|
||||
import { app } from "/scripts/app.js";
|
||||
|
||||
/**
|
||||
* Icons for the inspector panel
|
||||
*/
|
||||
const Icons = {
|
||||
chevronDown: `<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>`,
|
||||
};
|
||||
|
||||
/**
|
||||
* Inspector state
|
||||
*/
|
||||
interface InspectorState {
|
||||
cursor: { x: number; y: number };
|
||||
canvasScale: number;
|
||||
hoveredNode: any | null;
|
||||
}
|
||||
|
||||
let inspectorState: InspectorState = {
|
||||
cursor: { x: 0, y: 0 },
|
||||
canvasScale: 1,
|
||||
hoveredNode: null
|
||||
};
|
||||
|
||||
let inspectorBody: HTMLElement | null = null;
|
||||
let updateInterval: number | null = null;
|
||||
|
||||
/**
|
||||
* Format a number for display
|
||||
*/
|
||||
function formatNumber(value: number, decimals: number = 0): string {
|
||||
return value.toFixed(decimals);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently hovered node from the graph
|
||||
*/
|
||||
function getHoveredNode(): any | null {
|
||||
try {
|
||||
const graph = app.graph;
|
||||
if (!graph || !graph.canvas) return null;
|
||||
|
||||
const canvas = graph.canvas;
|
||||
const mousePos = canvas.graph_mouse;
|
||||
|
||||
if (!mousePos) return null;
|
||||
|
||||
// Find node at mouse position
|
||||
const nodes = graph._nodes;
|
||||
for (let i = nodes.length - 1; i >= 0; i--) {
|
||||
const node = nodes[i];
|
||||
if (node.pos && node.size) {
|
||||
const [x, y] = node.pos;
|
||||
const [w, h] = node.size;
|
||||
if (mousePos[0] >= x && mousePos[0] <= x + w &&
|
||||
mousePos[1] >= y && mousePos[1] <= y + h) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get canvas information
|
||||
*/
|
||||
function getCanvasInfo(): { scale: number; cursor: { x: number; y: number } } {
|
||||
try {
|
||||
const graph = app.graph;
|
||||
if (!graph || !graph.canvas) {
|
||||
return { scale: 1, cursor: { x: 0, y: 0 } };
|
||||
}
|
||||
|
||||
const canvas = graph.canvas;
|
||||
const scale = canvas.ds?.scale || 1;
|
||||
const mousePos = canvas.graph_mouse || [0, 0];
|
||||
|
||||
return {
|
||||
scale: scale * 100,
|
||||
cursor: { x: mousePos[0] || 0, y: mousePos[1] || 0 }
|
||||
};
|
||||
} catch {
|
||||
return { scale: 1, cursor: { x: 0, y: 0 } };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an inspector row
|
||||
*/
|
||||
function createRow(label: string, value: string): HTMLElement {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'magnify-inspector-row';
|
||||
|
||||
const labelEl = document.createElement('span');
|
||||
labelEl.className = 'magnify-inspector-label';
|
||||
labelEl.textContent = label;
|
||||
|
||||
const valueEl = document.createElement('span');
|
||||
valueEl.className = 'magnify-inspector-value';
|
||||
valueEl.textContent = value;
|
||||
valueEl.title = value;
|
||||
|
||||
row.appendChild(labelEl);
|
||||
row.appendChild(valueEl);
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a group with title
|
||||
*/
|
||||
function createGroup(title: string): HTMLElement {
|
||||
const group = document.createElement('div');
|
||||
group.className = 'magnify-inspector-group';
|
||||
|
||||
const titleEl = document.createElement('div');
|
||||
titleEl.className = 'magnify-inspector-group-title';
|
||||
titleEl.textContent = title;
|
||||
|
||||
group.appendChild(titleEl);
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the inspector display
|
||||
*/
|
||||
function updateInspector(): void {
|
||||
if (!inspectorBody) return;
|
||||
|
||||
const canvasInfo = getCanvasInfo();
|
||||
const hoveredNode = getHoveredNode();
|
||||
|
||||
// Clear body
|
||||
inspectorBody.innerHTML = '';
|
||||
|
||||
// Canvas info group
|
||||
const canvasGroup = createGroup('Canvas');
|
||||
canvasGroup.appendChild(createRow('Cursor', `(${formatNumber(canvasInfo.cursor.x)}, ${formatNumber(canvasInfo.cursor.y)})`));
|
||||
canvasGroup.appendChild(createRow('Scale', `${formatNumber(canvasInfo.scale, 1)}%`));
|
||||
inspectorBody.appendChild(canvasGroup);
|
||||
|
||||
// MagnifyGlass info
|
||||
const magnifyGlass = window.comfyUIMagnifyGlass;
|
||||
if (magnifyGlass) {
|
||||
const glassGroup = createGroup('Magnify Glass');
|
||||
glassGroup.appendChild(createRow('Zoom', `${formatNumber((magnifyGlass.config?.zoomFactor || 1) * 100)}%`));
|
||||
glassGroup.appendChild(createRow('Active', magnifyGlass.state?.active ? 'Yes' : 'No'));
|
||||
inspectorBody.appendChild(glassGroup);
|
||||
}
|
||||
|
||||
// Node info group
|
||||
if (hoveredNode) {
|
||||
const nodeGroup = createGroup('Hovered Node');
|
||||
|
||||
// Node badge
|
||||
const badge = document.createElement('div');
|
||||
badge.className = 'magnify-node-badge';
|
||||
badge.textContent = hoveredNode.type || 'Unknown';
|
||||
nodeGroup.appendChild(badge);
|
||||
|
||||
nodeGroup.appendChild(createRow('Title', hoveredNode.title || 'Untitled'));
|
||||
nodeGroup.appendChild(createRow('ID', String(hoveredNode.id)));
|
||||
|
||||
// Position
|
||||
if (hoveredNode.pos) {
|
||||
nodeGroup.appendChild(createRow('Position', `(${formatNumber(hoveredNode.pos[0])}, ${formatNumber(hoveredNode.pos[1])})`));
|
||||
}
|
||||
|
||||
// Size
|
||||
if (hoveredNode.size) {
|
||||
nodeGroup.appendChild(createRow('Size', `${formatNumber(hoveredNode.size[0])} × ${formatNumber(hoveredNode.size[1])}`));
|
||||
}
|
||||
|
||||
// Widgets (first few)
|
||||
if (hoveredNode.widgets && hoveredNode.widgets.length > 0) {
|
||||
const widgetGroup = createGroup('Parameters');
|
||||
const widgetsToShow = hoveredNode.widgets.slice(0, 6);
|
||||
for (const widget of widgetsToShow) {
|
||||
if (widget.name && widget.value !== undefined) {
|
||||
let value = String(widget.value);
|
||||
if (value.length > 30) value = value.substring(0, 27) + '...';
|
||||
widgetGroup.appendChild(createRow(widget.name, value));
|
||||
}
|
||||
}
|
||||
if (hoveredNode.widgets.length > 6) {
|
||||
const moreEl = document.createElement('div');
|
||||
moreEl.style.cssText = 'font-size: 11px; color: #666; padding: 4px 0;';
|
||||
moreEl.textContent = `+${hoveredNode.widgets.length - 6} more...`;
|
||||
widgetGroup.appendChild(moreEl);
|
||||
}
|
||||
inspectorBody.appendChild(widgetGroup);
|
||||
}
|
||||
|
||||
inspectorBody.appendChild(nodeGroup);
|
||||
} else {
|
||||
// Empty state
|
||||
const empty = document.createElement('div');
|
||||
empty.className = 'magnify-inspector-empty';
|
||||
empty.textContent = 'Hover over a node to inspect';
|
||||
inspectorBody.appendChild(empty);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the inspector panel into the container
|
||||
*/
|
||||
export function renderInspectorPanel(container: HTMLElement): void {
|
||||
const section = document.createElement('div');
|
||||
section.className = 'magnify-sidebar-section';
|
||||
|
||||
// Section header
|
||||
const header = document.createElement('div');
|
||||
header.className = 'magnify-sidebar-section-header';
|
||||
header.innerHTML = `${Icons.chevronDown}<span>Inspector</span>`;
|
||||
|
||||
// Section body
|
||||
const body = document.createElement('div');
|
||||
body.className = 'magnify-sidebar-section-body magnify-inspector-section';
|
||||
inspectorBody = body;
|
||||
|
||||
// Toggle collapse
|
||||
header.addEventListener('click', () => {
|
||||
header.classList.toggle('collapsed');
|
||||
body.classList.toggle('collapsed');
|
||||
});
|
||||
|
||||
section.appendChild(header);
|
||||
section.appendChild(body);
|
||||
container.appendChild(section);
|
||||
|
||||
// Initial update
|
||||
updateInspector();
|
||||
|
||||
// Start update interval
|
||||
if (updateInterval) {
|
||||
clearInterval(updateInterval);
|
||||
}
|
||||
updateInterval = window.setInterval(updateInspector, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup the inspector (call when sidebar is destroyed)
|
||||
*/
|
||||
export function cleanupInspector(): void {
|
||||
if (updateInterval) {
|
||||
clearInterval(updateInterval);
|
||||
updateInterval = null;
|
||||
}
|
||||
inspectorBody = null;
|
||||
}
|
||||
@@ -0,0 +1,678 @@
|
||||
/**
|
||||
* ComfyUI MagnifyGlass - Sidebar Settings Panel
|
||||
*
|
||||
* All settings controls for the sidebar.
|
||||
*/
|
||||
|
||||
// @ts-ignore
|
||||
import { app } from "/scripts/app.js";
|
||||
import {
|
||||
GLASS_POSITIONS,
|
||||
GLASS_SHAPES,
|
||||
ACTIVATION_KEYS,
|
||||
RESET_KEYS,
|
||||
TOGGLE_FOLLOW_KEYS,
|
||||
PANEL_POSITIONS
|
||||
} from '../shared/constants';
|
||||
|
||||
/**
|
||||
* Icons for the settings panel
|
||||
*/
|
||||
const Icons = {
|
||||
chevronDown: `<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>`,
|
||||
reset: `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="1 4 1 10 7 10"></polyline><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"></path></svg>`,
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a slider control with tooltip
|
||||
*/
|
||||
/**
|
||||
* Create a slider control with tooltip
|
||||
*/
|
||||
function createSlider(
|
||||
label: string,
|
||||
value: number,
|
||||
min: number,
|
||||
max: number,
|
||||
step: number,
|
||||
unit: string,
|
||||
onChange: (value: number) => void,
|
||||
tooltip?: string,
|
||||
onInput?: (value: number) => void
|
||||
): HTMLElement {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'magnify-control-row';
|
||||
if (tooltip) row.title = tooltip;
|
||||
|
||||
const labelRow = document.createElement('div');
|
||||
labelRow.className = 'magnify-control-label-row';
|
||||
|
||||
const labelEl = document.createElement('label');
|
||||
labelEl.textContent = label;
|
||||
|
||||
const valueEl = document.createElement('span');
|
||||
valueEl.className = 'control-value';
|
||||
valueEl.textContent = `${value}${unit}`;
|
||||
|
||||
labelRow.appendChild(labelEl);
|
||||
labelRow.appendChild(valueEl);
|
||||
|
||||
const slider = document.createElement('input');
|
||||
slider.type = 'range';
|
||||
slider.className = 'magnify-slider';
|
||||
slider.min = String(min);
|
||||
slider.max = String(max);
|
||||
slider.step = String(step);
|
||||
slider.value = String(value);
|
||||
|
||||
// Prevent ComfyUI from capturing mouse events during drag
|
||||
slider.addEventListener('mousedown', (e) => e.stopPropagation());
|
||||
slider.addEventListener('touchstart', (e) => e.stopPropagation());
|
||||
slider.addEventListener('pointerdown', (e) => e.stopPropagation());
|
||||
|
||||
slider.addEventListener('input', (e) => {
|
||||
e.stopPropagation();
|
||||
const newValue = parseFloat(slider.value);
|
||||
valueEl.textContent = `${newValue}${unit}`;
|
||||
if (onInput) onInput(newValue);
|
||||
});
|
||||
|
||||
slider.addEventListener('change', (e) => {
|
||||
e.stopPropagation();
|
||||
const newValue = parseFloat(slider.value);
|
||||
onChange(newValue);
|
||||
});
|
||||
|
||||
row.appendChild(labelRow);
|
||||
row.appendChild(slider);
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a toggle control with tooltip
|
||||
*/
|
||||
function createToggle(
|
||||
label: string,
|
||||
checked: boolean,
|
||||
onChange: (checked: boolean) => void,
|
||||
tooltip?: string
|
||||
): HTMLElement {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'magnify-toggle-row';
|
||||
if (tooltip) row.title = tooltip;
|
||||
|
||||
const labelEl = document.createElement('label');
|
||||
labelEl.textContent = label;
|
||||
|
||||
const toggle = document.createElement('div');
|
||||
toggle.className = `magnify-toggle${checked ? ' active' : ''}`;
|
||||
|
||||
toggle.addEventListener('click', () => {
|
||||
const isActive = toggle.classList.toggle('active');
|
||||
onChange(isActive);
|
||||
});
|
||||
|
||||
row.appendChild(labelEl);
|
||||
row.appendChild(toggle);
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a select dropdown control with tooltip
|
||||
*/
|
||||
function createSelect(
|
||||
label: string,
|
||||
value: string,
|
||||
options: readonly string[] | string[],
|
||||
onChange: (value: string) => void,
|
||||
tooltip?: string
|
||||
): HTMLElement {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'magnify-control-row';
|
||||
if (tooltip) row.title = tooltip;
|
||||
|
||||
const labelEl = document.createElement('label');
|
||||
labelEl.textContent = label;
|
||||
|
||||
const select = document.createElement('select');
|
||||
select.className = 'magnify-select';
|
||||
|
||||
options.forEach(opt => {
|
||||
const option = document.createElement('option');
|
||||
option.value = opt;
|
||||
option.textContent = opt;
|
||||
if (opt === value) option.selected = true;
|
||||
select.appendChild(option);
|
||||
});
|
||||
|
||||
select.addEventListener('change', () => onChange(select.value));
|
||||
|
||||
row.appendChild(labelEl);
|
||||
row.appendChild(select);
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a color picker control with tooltip
|
||||
*/
|
||||
/**
|
||||
* Create a color picker control with tooltip
|
||||
*/
|
||||
function createColorPicker(
|
||||
label: string,
|
||||
value: string,
|
||||
onChange: (value: string) => void,
|
||||
tooltip?: string,
|
||||
onInput?: (value: string) => void
|
||||
): HTMLElement {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'magnify-control-row magnify-color-row';
|
||||
if (tooltip) row.title = tooltip;
|
||||
|
||||
const labelEl = document.createElement('label');
|
||||
labelEl.textContent = label;
|
||||
|
||||
const colorWrapper = document.createElement('div');
|
||||
colorWrapper.className = 'magnify-color-wrapper';
|
||||
|
||||
const colorInput = document.createElement('input');
|
||||
colorInput.type = 'color';
|
||||
colorInput.className = 'magnify-color-input';
|
||||
colorInput.value = value;
|
||||
|
||||
const colorPreview = document.createElement('span');
|
||||
colorPreview.className = 'magnify-color-preview';
|
||||
colorPreview.textContent = value;
|
||||
|
||||
colorInput.addEventListener('input', () => {
|
||||
colorPreview.textContent = colorInput.value;
|
||||
if (onInput) onInput(colorInput.value);
|
||||
});
|
||||
|
||||
colorInput.addEventListener('change', () => {
|
||||
onChange(colorInput.value);
|
||||
});
|
||||
|
||||
colorWrapper.appendChild(colorInput);
|
||||
colorWrapper.appendChild(colorPreview);
|
||||
|
||||
row.appendChild(labelEl);
|
||||
row.appendChild(colorWrapper);
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current MagnifyGlass settings from app.ui.settings
|
||||
*/
|
||||
function getSettingValue(id: string, defaultValue: any): any {
|
||||
try {
|
||||
const value = app.ui.settings.getSettingValue(id);
|
||||
return value !== undefined ? value : defaultValue;
|
||||
} catch {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a MagnifyGlass setting via app.ui.settings
|
||||
*/
|
||||
function setSettingValue(id: string, value: any): void {
|
||||
try {
|
||||
app.ui.settings.setSettingValue(id, value);
|
||||
} catch (e) {
|
||||
console.warn(`Failed to set setting ${id}:`, e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Storage key for section states */
|
||||
const SECTION_STATE_KEY = 'magnifyglass-sidebar-sections';
|
||||
|
||||
/**
|
||||
* Get saved section states from localStorage
|
||||
*/
|
||||
function getSectionStates(): Record<string, boolean> {
|
||||
try {
|
||||
const saved = localStorage.getItem(SECTION_STATE_KEY);
|
||||
return saved ? JSON.parse(saved) : {};
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save section state to localStorage
|
||||
*/
|
||||
function saveSectionState(title: string, collapsed: boolean): void {
|
||||
try {
|
||||
const states = getSectionStates();
|
||||
states[title] = collapsed;
|
||||
localStorage.setItem(SECTION_STATE_KEY, JSON.stringify(states));
|
||||
} catch {
|
||||
// Ignore storage errors
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a collapsible section with persistent state
|
||||
*/
|
||||
function createSection(title: string, defaultCollapsed: boolean = false): { section: HTMLElement; body: HTMLElement } {
|
||||
const section = document.createElement('div');
|
||||
section.className = 'magnify-sidebar-section';
|
||||
|
||||
// Check if we have a saved state for this section
|
||||
const savedStates = getSectionStates();
|
||||
const collapsed = savedStates[title] !== undefined ? savedStates[title] : defaultCollapsed;
|
||||
|
||||
const header = document.createElement('div');
|
||||
header.className = `magnify-sidebar-section-header${collapsed ? ' collapsed' : ''}`;
|
||||
header.innerHTML = `${Icons.chevronDown}<span>${title}</span>`;
|
||||
|
||||
const body = document.createElement('div');
|
||||
body.className = `magnify-sidebar-section-body${collapsed ? ' collapsed' : ''}`;
|
||||
|
||||
header.addEventListener('click', () => {
|
||||
const isCollapsed = header.classList.toggle('collapsed');
|
||||
body.classList.toggle('collapsed');
|
||||
// Save the new state
|
||||
saveSectionState(title, isCollapsed);
|
||||
});
|
||||
|
||||
section.appendChild(header);
|
||||
section.appendChild(body);
|
||||
|
||||
return { section, body };
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the settings panel into the container
|
||||
*/
|
||||
export function renderSettingsPanel(container: HTMLElement): void {
|
||||
// Get current hotkey values for tooltips
|
||||
const activationKey = getSettingValue('🔍MagnifyGlass.ActivationKey', 'x').toUpperCase();
|
||||
const resetKey = getSettingValue('🔍MagnifyGlass.ResetKey', 'o').toUpperCase();
|
||||
const toggleFollowKey = getSettingValue('🔍MagnifyGlass.ToggleFollowCursorKey', 'h').toUpperCase();
|
||||
|
||||
// ===== Glass Settings Section =====
|
||||
const glassSection = createSection('Glass Settings');
|
||||
|
||||
glassSection.body.appendChild(createSlider('Zoom Factor',
|
||||
getSettingValue('🔍MagnifyGlass.ZoomFactor', 300), 100, 1000, 25, '%',
|
||||
(value) => setSettingValue('🔍MagnifyGlass.ZoomFactor', value),
|
||||
'Magnification level of the glass (100-1000%)',
|
||||
(value) => {
|
||||
const mg = (window as any).comfyUIMagnifyGlass;
|
||||
if (mg?.config) {
|
||||
mg.config.zoomFactor = value / 100;
|
||||
if (mg.state.active) mg.updateMagnifiedView();
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
glassSection.body.appendChild(createSlider('Glass Size',
|
||||
getSettingValue('🔍MagnifyGlass.GlassSize', 250), 50, 500, 10, 'px',
|
||||
(value) => setSettingValue('🔍MagnifyGlass.GlassSize', value),
|
||||
'Size of the magnifying glass in pixels',
|
||||
(value) => {
|
||||
const mg = (window as any).comfyUIMagnifyGlass;
|
||||
if (mg?.config) {
|
||||
mg.config.glassSize = value;
|
||||
mg.applyUiChanges();
|
||||
if (mg.state.active) mg.updateMagnifiedView();
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
glassSection.body.appendChild(createSelect('Shape',
|
||||
getSettingValue('🔍MagnifyGlass.GlassShape', 'Rounded Square'), GLASS_SHAPES,
|
||||
(value) => setSettingValue('🔍MagnifyGlass.GlassShape', value),
|
||||
'Shape of the magnifying glass'
|
||||
));
|
||||
|
||||
glassSection.body.appendChild(createSelect('Position',
|
||||
getSettingValue('🔍MagnifyGlass.GlassPosition', 'Top-Right'), GLASS_POSITIONS,
|
||||
(value) => setSettingValue('🔍MagnifyGlass.GlassPosition', value),
|
||||
`Default position of the glass. Press ${resetKey} to reset.`
|
||||
));
|
||||
|
||||
glassSection.body.appendChild(createSelect('Filtering',
|
||||
getSettingValue('🔍MagnifyGlass.TextureFiltering', 'Linear'), ['Linear', 'Nearest'],
|
||||
(value) => setSettingValue('🔍MagnifyGlass.TextureFiltering', value),
|
||||
'Texture filtering: Linear (smooth) or Nearest (pixelated)'
|
||||
));
|
||||
|
||||
glassSection.body.appendChild(createSlider('Border Width',
|
||||
getSettingValue('🔍MagnifyGlass.BorderWidth', 2), 0, 10, 0.5, 'px',
|
||||
(value) => setSettingValue('🔍MagnifyGlass.BorderWidth', value),
|
||||
'Width of the glass border in pixels',
|
||||
(value) => {
|
||||
const mg = (window as any).comfyUIMagnifyGlass;
|
||||
if (mg?.config) {
|
||||
mg.config.borderWidth = value;
|
||||
mg.applyUiChanges();
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
glassSection.body.appendChild(createColorPicker('Border Color',
|
||||
getSettingValue('🔍MagnifyGlass.BorderColor', '#ffffff'),
|
||||
(value) => setSettingValue('🔍MagnifyGlass.BorderColor', value),
|
||||
'Color of the glass border',
|
||||
(value) => {
|
||||
const mg = (window as any).comfyUIMagnifyGlass;
|
||||
if (mg?.config) {
|
||||
mg.config.borderColor = value;
|
||||
mg.applyUiChanges();
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
glassSection.body.appendChild(createToggle('Show Border',
|
||||
getSettingValue('🔍MagnifyGlass.BorderEnabled', true),
|
||||
(checked) => setSettingValue('🔍MagnifyGlass.BorderEnabled', checked),
|
||||
'Show or hide the glass border'
|
||||
));
|
||||
|
||||
glassSection.body.appendChild(createToggle('Follow Cursor',
|
||||
getSettingValue('🔍MagnifyGlass.FollowCursor', false),
|
||||
(checked) => setSettingValue('🔍MagnifyGlass.FollowCursor', checked),
|
||||
`Glass follows the cursor. Hotkey: ${toggleFollowKey}`
|
||||
));
|
||||
|
||||
glassSection.body.appendChild(createToggle('Always Active Mode',
|
||||
getSettingValue('🔍MagnifyGlass.AlwaysActiveMode', true),
|
||||
(checked) => setSettingValue('🔍MagnifyGlass.AlwaysActiveMode', checked),
|
||||
`When enabled, glass stays visible. Toggle with ${activationKey}`
|
||||
));
|
||||
|
||||
container.appendChild(glassSection.section);
|
||||
|
||||
// ===== Hotkeys Section =====
|
||||
const hotkeySection = createSection('Hotkeys', true);
|
||||
|
||||
hotkeySection.body.appendChild(createSelect('Activation Key',
|
||||
getSettingValue('🔍MagnifyGlass.ActivationKey', 'x'), ACTIVATION_KEYS,
|
||||
(value) => setSettingValue('🔍MagnifyGlass.ActivationKey', value),
|
||||
'Key to toggle or hold to activate the magnifying glass'
|
||||
));
|
||||
|
||||
hotkeySection.body.appendChild(createSelect('Reset Key',
|
||||
getSettingValue('🔍MagnifyGlass.ResetKey', 'o'), RESET_KEYS,
|
||||
(value) => setSettingValue('🔍MagnifyGlass.ResetKey', value),
|
||||
'Key to reset glass position and disable follow cursor'
|
||||
));
|
||||
|
||||
hotkeySection.body.appendChild(createSelect('Toggle Follow Key',
|
||||
getSettingValue('🔍MagnifyGlass.ToggleFollowCursorKey', 'h'), TOGGLE_FOLLOW_KEYS,
|
||||
(value) => setSettingValue('🔍MagnifyGlass.ToggleFollowCursorKey', value),
|
||||
'Key to toggle follow cursor mode'
|
||||
));
|
||||
|
||||
hotkeySection.body.appendChild(createToggle('Require Alt Key',
|
||||
getSettingValue('🔍MagnifyGlass.AltRequired', false),
|
||||
(checked) => setSettingValue('🔍MagnifyGlass.AltRequired', checked),
|
||||
'Require Alt key to be held with activation key'
|
||||
));
|
||||
|
||||
hotkeySection.body.appendChild(createSlider('Offset Step',
|
||||
getSettingValue('🔍MagnifyGlass.OffsetStep', 10), 1, 50, 1, 'px',
|
||||
(value) => setSettingValue('🔍MagnifyGlass.OffsetStep', value),
|
||||
'Amount to move glass per arrow key press (pixels)'
|
||||
));
|
||||
|
||||
container.appendChild(hotkeySection.section);
|
||||
|
||||
// ===== Info Panel Section =====
|
||||
const panelSection = createSection('Info Panel', true);
|
||||
|
||||
const toggleHotkey = getSettingValue('🔍MagnifyGlass.ToggleHotkey', 'i').toUpperCase();
|
||||
const glassToggleKey = getSettingValue('🔍MagnifyGlass.GlassPreviewToggleHotkey', 'g').toUpperCase();
|
||||
const pinHotkeyVal = getSettingValue('🔍MagnifyGlass.PinPanelHotkey', 'u').toUpperCase();
|
||||
|
||||
panelSection.body.appendChild(createToggle('Enable Panel',
|
||||
getSettingValue('🔍MagnifyGlass.InfoPanelEnabled', true),
|
||||
(checked) => setSettingValue('🔍MagnifyGlass.InfoPanelEnabled', checked),
|
||||
`Show info panel with node details. Toggle: ${toggleHotkey}`
|
||||
));
|
||||
|
||||
panelSection.body.appendChild(createSelect('Position',
|
||||
getSettingValue('🔍MagnifyGlass.InfoPanelPosition', 'Bottom'), PANEL_POSITIONS,
|
||||
(value) => setSettingValue('🔍MagnifyGlass.InfoPanelPosition', value),
|
||||
'Position of the info panel relative to the glass'
|
||||
));
|
||||
|
||||
panelSection.body.appendChild(createSlider('Width',
|
||||
getSettingValue('🔍MagnifyGlass.InfoPanelWidth', 300), 200, 600, 20, 'px',
|
||||
(value) => setSettingValue('🔍MagnifyGlass.InfoPanelWidth', value),
|
||||
'Width of the info panel',
|
||||
(value) => {
|
||||
const infoPanel = (window as any).infoPanelManager;
|
||||
if (infoPanel?.stateManager?.state?.settings) {
|
||||
infoPanel.stateManager.state.settings['🔍MagnifyGlass.InfoPanelWidth'] = value;
|
||||
infoPanel.uiManager.applyStyles();
|
||||
|
||||
// Ensure visible and reflow if active
|
||||
if (infoPanel.magnifyGlass.state.active) {
|
||||
infoPanel.uiManager.show();
|
||||
infoPanel.positionManager.positionPanel();
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
panelSection.body.appendChild(createSlider('Max Height',
|
||||
getSettingValue('🔍MagnifyGlass.InfoPanelMaxHeight', 300), 200, 1500, 50, 'px',
|
||||
(value) => setSettingValue('🔍MagnifyGlass.InfoPanelMaxHeight', value),
|
||||
'Maximum height of the info panel',
|
||||
(value) => {
|
||||
console.log('[MaxHeight] Handler called with value:', value);
|
||||
const infoPanel = (window as any).infoPanelManager;
|
||||
console.log('[MaxHeight] infoPanelManager:', infoPanel ? 'found' : 'NOT FOUND');
|
||||
if (infoPanel?.stateManager?.state?.settings) {
|
||||
console.log('[MaxHeight] Updating setting and calling applyStyles');
|
||||
infoPanel.stateManager.state.settings['🔍MagnifyGlass.InfoPanelMaxHeight'] = value;
|
||||
infoPanel.uiManager.applyStyles();
|
||||
|
||||
// Ensure visible and reflow if active
|
||||
if (infoPanel.magnifyGlass.state.active) {
|
||||
console.log('[MaxHeight] Glass is active, calling show() and positionPanel()');
|
||||
infoPanel.uiManager.show();
|
||||
infoPanel.positionManager.positionPanel();
|
||||
} else {
|
||||
console.log('[MaxHeight] Glass is NOT active, skipping show()');
|
||||
}
|
||||
} else {
|
||||
console.warn('[MaxHeight] Check failed - stateManager/state/settings missing');
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
panelSection.body.appendChild(createSlider('Opacity',
|
||||
getSettingValue('🔍MagnifyGlass.InfoPanelOpacity', 95), 10, 100, 5, '%',
|
||||
(value) => setSettingValue('🔍MagnifyGlass.InfoPanelOpacity', value),
|
||||
'Background opacity of the info panel',
|
||||
(value) => {
|
||||
const infoPanel = (window as any).infoPanelManager;
|
||||
if (infoPanel?.stateManager?.state?.settings) {
|
||||
infoPanel.stateManager.state.settings['🔍MagnifyGlass.InfoPanelOpacity'] = value;
|
||||
infoPanel.uiManager.applyStyles();
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
panelSection.body.appendChild(createColorPicker('Text Color',
|
||||
getSettingValue('🔍MagnifyGlass.InfoPanelTextColor', '#6b7280'),
|
||||
(value) => setSettingValue('🔍MagnifyGlass.InfoPanelTextColor', value),
|
||||
'Text color in the info panel',
|
||||
(value) => {
|
||||
const infoPanel = (window as any).infoPanelManager;
|
||||
if (infoPanel?.stateManager?.state?.settings) {
|
||||
infoPanel.stateManager.state.settings['🔍MagnifyGlass.InfoPanelTextColor'] = value;
|
||||
infoPanel.uiManager.applyStyles();
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
panelSection.body.appendChild(createColorPicker('Accent Color',
|
||||
getSettingValue('🔍MagnifyGlass.InfoPanelAccentColor', '#3b82f6'),
|
||||
(value) => setSettingValue('🔍MagnifyGlass.InfoPanelAccentColor', value),
|
||||
'Accent color for highlights',
|
||||
(value) => {
|
||||
const infoPanel = (window as any).infoPanelManager;
|
||||
if (infoPanel?.stateManager?.state?.settings) {
|
||||
infoPanel.stateManager.state.settings['🔍MagnifyGlass.InfoPanelAccentColor'] = value;
|
||||
infoPanel.uiManager.applyStyles();
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
panelSection.body.appendChild(createToggle('Animations',
|
||||
getSettingValue('🔍MagnifyGlass.InfoPanelAnimations', false),
|
||||
(checked) => setSettingValue('🔍MagnifyGlass.InfoPanelAnimations', checked),
|
||||
'Enable smooth animations for panel updates'
|
||||
));
|
||||
|
||||
panelSection.body.appendChild(createToggle('Hover Controls',
|
||||
getSettingValue('🔍MagnifyGlass.ShowHoveringControls', true),
|
||||
(checked) => setSettingValue('🔍MagnifyGlass.ShowHoveringControls', checked),
|
||||
'Show floating control buttons near the panel'
|
||||
));
|
||||
|
||||
panelSection.body.appendChild(createSelect('Controls Position',
|
||||
getSettingValue('🔍MagnifyGlass.ControlsPosition', 'bottom'),
|
||||
['top', 'bottom', 'left', 'right', 'top-left', 'top-right', 'bottom-left', 'bottom-right'],
|
||||
(value) => setSettingValue('🔍MagnifyGlass.ControlsPosition', value),
|
||||
'Position of floating control buttons'
|
||||
));
|
||||
|
||||
container.appendChild(panelSection.section);
|
||||
|
||||
// ===== Panel Hotkeys Section =====
|
||||
const panelHotkeySection = createSection('Panel Hotkeys', true);
|
||||
|
||||
panelHotkeySection.body.appendChild(createSelect('Toggle Key',
|
||||
getSettingValue('🔍MagnifyGlass.ToggleHotkey', 'i'), ['i', 'j', 'k', 'l', 'n', 'm'],
|
||||
(value) => setSettingValue('🔍MagnifyGlass.ToggleHotkey', value),
|
||||
'Key to toggle info panel visibility'
|
||||
));
|
||||
|
||||
panelHotkeySection.body.appendChild(createSelect('Glass Toggle Key',
|
||||
getSettingValue('🔍MagnifyGlass.GlassPreviewToggleHotkey', 'g'), ['g', 'f', 'v', 'b'],
|
||||
(value) => setSettingValue('🔍MagnifyGlass.GlassPreviewToggleHotkey', value),
|
||||
'Key to toggle glass preview in panel'
|
||||
));
|
||||
|
||||
panelHotkeySection.body.appendChild(createSelect('Pin Key',
|
||||
getSettingValue('🔍MagnifyGlass.PinPanelHotkey', 'u'), ['u', 'p', 'y', 't'],
|
||||
(value) => setSettingValue('🔍MagnifyGlass.PinPanelHotkey', value),
|
||||
'Key to pin/unpin the info panel position'
|
||||
));
|
||||
|
||||
container.appendChild(panelHotkeySection.section);
|
||||
|
||||
// ===== Debug Section =====
|
||||
const debugSection = createSection('Debug', true);
|
||||
|
||||
debugSection.body.appendChild(createToggle('Debug Mode',
|
||||
getSettingValue('🔍MagnifyGlass.DebugMode', false),
|
||||
(checked) => setSettingValue('🔍MagnifyGlass.DebugMode', checked),
|
||||
'Enable debug logging and visual indicators'
|
||||
));
|
||||
|
||||
container.appendChild(debugSection.section);
|
||||
|
||||
// ===== Button Row =====
|
||||
const buttonRow = document.createElement('div');
|
||||
buttonRow.style.padding = '16px';
|
||||
buttonRow.style.borderTop = '1px solid var(--border-color, #333)';
|
||||
buttonRow.style.display = 'flex';
|
||||
buttonRow.style.flexDirection = 'column';
|
||||
buttonRow.style.gap = '8px';
|
||||
|
||||
// Reset Position Button
|
||||
const resetBtn = document.createElement('button');
|
||||
resetBtn.className = 'magnify-btn';
|
||||
resetBtn.style.width = '100%';
|
||||
resetBtn.innerHTML = `${Icons.reset} Reset Position`;
|
||||
resetBtn.title = `Reset glass position and disable follow cursor. Hotkey: ${resetKey}`;
|
||||
|
||||
resetBtn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
const magnifyGlass = window.comfyUIMagnifyGlass;
|
||||
if (magnifyGlass && magnifyGlass.resetOffsets) {
|
||||
magnifyGlass.resetOffsets();
|
||||
// Update follow cursor toggle in UI
|
||||
const followToggles = container.querySelectorAll('.magnify-toggle-row');
|
||||
followToggles.forEach(row => {
|
||||
const label = row.querySelector('label');
|
||||
if (label && label.textContent === 'Follow Cursor') {
|
||||
const toggle = row.querySelector('.magnify-toggle');
|
||||
if (toggle) toggle.classList.remove('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
buttonRow.appendChild(resetBtn);
|
||||
|
||||
// Reset All Settings Button
|
||||
const resetAllBtn = document.createElement('button');
|
||||
resetAllBtn.className = 'magnify-btn magnify-btn-secondary';
|
||||
resetAllBtn.style.width = '100%';
|
||||
resetAllBtn.innerHTML = `${Icons.reset} Reset All Settings`;
|
||||
resetAllBtn.title = 'Reset all settings to their default values';
|
||||
|
||||
resetAllBtn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
// Confirm with user
|
||||
if (!confirm('Reset all MagnifyGlass settings to defaults?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Default glass settings
|
||||
setSettingValue('🔍MagnifyGlass.ZoomFactor', 300);
|
||||
setSettingValue('🔍MagnifyGlass.GlassSize', 250);
|
||||
setSettingValue('🔍MagnifyGlass.GlassShape', 'Rounded Square');
|
||||
setSettingValue('🔍MagnifyGlass.GlassPosition', 'Top-Right');
|
||||
setSettingValue('🔍MagnifyGlass.TextureFiltering', 'Linear');
|
||||
setSettingValue('🔍MagnifyGlass.BorderWidth', 2);
|
||||
setSettingValue('🔍MagnifyGlass.BorderColor', '#ffffff');
|
||||
setSettingValue('🔍MagnifyGlass.BorderEnabled', true);
|
||||
setSettingValue('🔍MagnifyGlass.FollowCursor', false);
|
||||
setSettingValue('🔍MagnifyGlass.AlwaysActiveMode', true);
|
||||
setSettingValue('🔍MagnifyGlass.ActivationKey', 'x');
|
||||
setSettingValue('🔍MagnifyGlass.ResetKey', 'o');
|
||||
setSettingValue('🔍MagnifyGlass.ToggleFollowCursorKey', 'h');
|
||||
setSettingValue('🔍MagnifyGlass.AltRequired', false);
|
||||
setSettingValue('🔍MagnifyGlass.OffsetStep', 10);
|
||||
setSettingValue('🔍MagnifyGlass.DebugMode', false);
|
||||
|
||||
// Default panel settings
|
||||
setSettingValue('🔍MagnifyGlass.InfoPanelEnabled', true);
|
||||
setSettingValue('🔍MagnifyGlass.InfoPanelPosition', 'Bottom');
|
||||
setSettingValue('🔍MagnifyGlass.InfoPanelWidth', 300);
|
||||
setSettingValue('🔍MagnifyGlass.InfoPanelMaxHeight', 600);
|
||||
setSettingValue('🔍MagnifyGlass.InfoPanelOpacity', 95);
|
||||
setSettingValue('🔍MagnifyGlass.InfoPanelTextColor', '#6b7280');
|
||||
setSettingValue('🔍MagnifyGlass.InfoPanelAccentColor', '#3b82f6');
|
||||
setSettingValue('🔍MagnifyGlass.InfoPanelAnimations', false);
|
||||
setSettingValue('🔍MagnifyGlass.ShowHoveringControls', true);
|
||||
setSettingValue('🔍MagnifyGlass.ControlsPosition', 'bottom');
|
||||
setSettingValue('🔍MagnifyGlass.ToggleHotkey', 'i');
|
||||
setSettingValue('🔍MagnifyGlass.GlassPreviewToggleHotkey', 'g');
|
||||
setSettingValue('🔍MagnifyGlass.PinPanelHotkey', 'u');
|
||||
|
||||
// Also reset position
|
||||
const magnifyGlass = window.comfyUIMagnifyGlass;
|
||||
if (magnifyGlass && magnifyGlass.resetOffsets) {
|
||||
magnifyGlass.resetOffsets();
|
||||
}
|
||||
});
|
||||
|
||||
buttonRow.appendChild(resetAllBtn);
|
||||
container.appendChild(buttonRow);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* ComfyUI MagnifyGlass - Sidebar Index
|
||||
*
|
||||
* Re-exports all sidebar modules.
|
||||
*/
|
||||
|
||||
export { registerSidebar, initSidebar } from './sidebar';
|
||||
export { renderSettingsPanel } from './SidebarSettings';
|
||||
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* ComfyUI MagnifyGlass - Sidebar Entry Point
|
||||
*
|
||||
* Registers the MagnifyGlass sidebar tab with ComfyUI.
|
||||
*/
|
||||
|
||||
// @ts-ignore
|
||||
import { app } from "/scripts/app.js";
|
||||
import { renderSettingsPanel } from './SidebarSettings';
|
||||
|
||||
/**
|
||||
* Magnify glass icon SVG
|
||||
*/
|
||||
const MAGNIFY_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>`;
|
||||
|
||||
/** Track if sidebar has been registered */
|
||||
let sidebarRegistered = false;
|
||||
|
||||
/**
|
||||
* Load sidebar CSS
|
||||
*/
|
||||
function loadSidebarStyles(): void {
|
||||
if (!document.getElementById('magnify-sidebar-styles')) {
|
||||
const link = document.createElement('link');
|
||||
link.id = 'magnify-sidebar-styles';
|
||||
link.rel = 'stylesheet';
|
||||
link.type = 'text/css';
|
||||
link.href = 'extensions/comfyui-magnifyglass/sidebar.css';
|
||||
document.head.appendChild(link);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the sidebar content
|
||||
*/
|
||||
function renderSidebar(container: HTMLElement): void {
|
||||
// Load styles
|
||||
loadSidebarStyles();
|
||||
|
||||
// Clear existing content to prevent duplicates
|
||||
container.innerHTML = '';
|
||||
|
||||
// Create main container
|
||||
const sidebar = document.createElement('div');
|
||||
sidebar.className = 'magnify-sidebar';
|
||||
|
||||
// Header
|
||||
const header = document.createElement('div');
|
||||
header.className = 'magnify-sidebar-header';
|
||||
header.innerHTML = `${MAGNIFY_ICON}<h2>Magnify G.</h2>`;
|
||||
sidebar.appendChild(header);
|
||||
|
||||
// Content area
|
||||
const content = document.createElement('div');
|
||||
content.className = 'magnify-sidebar-content';
|
||||
|
||||
// Render settings panel (contains all settings)
|
||||
renderSettingsPanel(content);
|
||||
|
||||
sidebar.appendChild(content);
|
||||
container.appendChild(sidebar);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the sidebar tab with ComfyUI
|
||||
*/
|
||||
export function registerSidebar(): void {
|
||||
// Prevent duplicate registration
|
||||
if (sidebarRegistered) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait for app to be ready
|
||||
if (!app.extensionManager) {
|
||||
console.warn('MagnifyGlass: extensionManager not available, sidebar registration skipped');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
app.extensionManager.registerSidebarTab({
|
||||
id: 'magnifyglass',
|
||||
icon: 'pi pi-search',
|
||||
title: 'Magnify G.',
|
||||
tooltip: 'MagnifyGlass Settings',
|
||||
type: 'custom',
|
||||
render: (el: HTMLElement) => {
|
||||
renderSidebar(el);
|
||||
}
|
||||
});
|
||||
|
||||
sidebarRegistered = true;
|
||||
console.log('MagnifyGlass: Sidebar registered successfully');
|
||||
} catch (e) {
|
||||
console.warn('MagnifyGlass: Failed to register sidebar:', e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize sidebar (called after app setup)
|
||||
*/
|
||||
export function initSidebar(): void {
|
||||
// Register on next tick to ensure app is ready
|
||||
setTimeout(() => {
|
||||
registerSidebar();
|
||||
}, 100);
|
||||
}
|
||||
Vendored
+1
@@ -215,6 +215,7 @@ export interface MagnifyGlassInstance {
|
||||
updateMagnifiedView(): void;
|
||||
applyUiChanges(): void;
|
||||
toggle(): void;
|
||||
resetOffsets(): void;
|
||||
}
|
||||
|
||||
export interface MagnifyGlassState {
|
||||
|
||||
@@ -0,0 +1,664 @@
|
||||
/**
|
||||
* ComfyUI MagnifyGlass - Info Panel Styles
|
||||
*
|
||||
* Complete CSS styles for the info panel component.
|
||||
*/
|
||||
|
||||
/* SVG Icon Styling */
|
||||
.magnify-info-panel svg,
|
||||
.floating-controls svg {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: middle;
|
||||
fill: none;
|
||||
stroke: currentColor;
|
||||
pointer-events: none;
|
||||
/* Allow clicks to pass through to button */
|
||||
}
|
||||
|
||||
.header-icon svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.section-icon svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.control-btn svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.expand-icon svg {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.empty-state-icon svg {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
stroke-width: 1.5;
|
||||
}
|
||||
|
||||
.magnify-info-panel {
|
||||
border-radius: 12px;
|
||||
backdrop-filter: blur(20px);
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(255, 255, 255, 0.1);
|
||||
overflow: hidden;
|
||||
font-size: 16px;
|
||||
line-height: 1.6;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.magnify-info-panel * {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.magnify-info-panel.theme-dark {
|
||||
background: linear-gradient(135deg, rgba(0, 0, 0, 0.96), rgba(10, 10, 15, 0.96));
|
||||
color: var(--info-panel-text-color, #e0e0e0);
|
||||
border: 1px solid rgba(100, 100, 120, 0.4);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3), 0 2px 16px rgba(0, 0, 0, 0.2);
|
||||
backdrop-filter: blur(12px);
|
||||
}
|
||||
|
||||
.magnify-info-panel.theme-light {
|
||||
background: linear-gradient(135deg, rgba(250, 250, 255, 0.96), rgba(240, 240, 250, 0.96));
|
||||
color: var(--info-panel-text-color, #2a2a2a);
|
||||
border: 1px solid rgba(200, 200, 220, 0.5);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1), 0 2px 16px rgba(0, 0, 0, 0.05);
|
||||
backdrop-filter: blur(12px);
|
||||
}
|
||||
|
||||
/* Solarized Theme - warm, muted tones */
|
||||
.magnify-info-panel.theme-solarized {
|
||||
background: linear-gradient(135deg, rgba(0, 43, 54, 0.96), rgba(7, 54, 66, 0.96));
|
||||
color: var(--info-panel-text-color, #839496);
|
||||
border: 1px solid rgba(88, 110, 117, 0.5);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3), 0 2px 16px rgba(0, 0, 0, 0.2);
|
||||
backdrop-filter: blur(12px);
|
||||
--info-panel-accent-color: #268bd2;
|
||||
}
|
||||
|
||||
/* Arc Theme - modern blue-gray */
|
||||
.magnify-info-panel.theme-arc {
|
||||
background: linear-gradient(135deg, rgba(56, 60, 74, 0.96), rgba(64, 69, 82, 0.96));
|
||||
color: var(--info-panel-text-color, #d3dae3);
|
||||
border: 1px solid rgba(90, 100, 120, 0.5);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.25), 0 2px 16px rgba(0, 0, 0, 0.15);
|
||||
backdrop-filter: blur(12px);
|
||||
--info-panel-accent-color: #5294e2;
|
||||
}
|
||||
|
||||
/* Nord Theme - cool arctic colors */
|
||||
.magnify-info-panel.theme-nord {
|
||||
background: linear-gradient(135deg, rgba(46, 52, 64, 0.96), rgba(59, 66, 82, 0.96));
|
||||
color: var(--info-panel-text-color, #d8dee9);
|
||||
border: 1px solid rgba(76, 86, 106, 0.5);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.25), 0 2px 16px rgba(0, 0, 0, 0.15);
|
||||
backdrop-filter: blur(12px);
|
||||
--info-panel-accent-color: #88c0d0;
|
||||
}
|
||||
|
||||
/* GitHub Theme - clean, professional */
|
||||
.magnify-info-panel.theme-github {
|
||||
background: linear-gradient(135deg, rgba(255, 255, 255, 0.96), rgba(246, 248, 250, 0.96));
|
||||
color: var(--info-panel-text-color, #24292e);
|
||||
border: 1px solid rgba(225, 228, 232, 0.8);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1), 0 2px 16px rgba(0, 0, 0, 0.05);
|
||||
backdrop-filter: blur(12px);
|
||||
--info-panel-accent-color: #0366d6;
|
||||
}
|
||||
|
||||
.magnify-info-panel.visible {
|
||||
transform: translateY(0) !important;
|
||||
}
|
||||
|
||||
.magnify-info-panel.panel-pinned {
|
||||
cursor: grab;
|
||||
box-shadow: 0 0 0 2px rgba(255, 215, 0, 0.5), 0 20px 40px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.magnify-info-panel.panel-locked {
|
||||
cursor: default !important;
|
||||
box-shadow: 0 0 0 2px rgba(255, 69, 0, 0.5), 0 20px 40px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.magnify-info-panel.panel-locked::after {
|
||||
content: '📌';
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
font-size: 14px;
|
||||
opacity: 0.7;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.magnify-info-panel.panel-pinned.panel-dragging {
|
||||
cursor: grabbing;
|
||||
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.5), 0 0 0 2px rgba(255, 215, 0, 0.7);
|
||||
transform: scale(1.02) !important;
|
||||
z-index: 100001 !important;
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
.magnify-info-panel.panel-pinned::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(45deg, rgba(255, 215, 0, 0.05) 0%, transparent 100%);
|
||||
pointer-events: none;
|
||||
border-radius: inherit;
|
||||
}
|
||||
|
||||
.panel-dragging * {
|
||||
pointer-events: none !important;
|
||||
user-select: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
.magnify-info-panel.panel-minimized .panel-content {
|
||||
max-height: 0 !important;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.floating-controls {
|
||||
position: fixed;
|
||||
display: none;
|
||||
/* Hidden initially - JS will show when positioned */
|
||||
padding: 4px;
|
||||
background: rgba(40, 40, 50, 0.95);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 4px;
|
||||
opacity: 1;
|
||||
z-index: 100000;
|
||||
backdrop-filter: blur(8px);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.floating-controls.vertical-layout {
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.floating-controls.horizontal-layout {
|
||||
flex-direction: row;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
background: rgba(60, 60, 70, 0.8);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
color: #ddd;
|
||||
border-radius: 3px;
|
||||
transition: background-color 0.15s ease;
|
||||
}
|
||||
|
||||
.control-btn:hover {
|
||||
background: rgba(80, 80, 90, 0.9);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.control-btn:active {
|
||||
background: rgba(100, 100, 110, 0.9);
|
||||
}
|
||||
|
||||
.control-btn:disabled {
|
||||
background: rgba(40, 40, 50, 0.5);
|
||||
color: #666;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.control-btn.active {
|
||||
background: rgba(255, 200, 50, 0.3);
|
||||
border-color: rgba(255, 200, 50, 0.6);
|
||||
color: #ffcc33;
|
||||
}
|
||||
|
||||
.control-btn.lock-btn.active {
|
||||
background: rgba(255, 69, 0, 0.3);
|
||||
border-color: rgba(255, 69, 0, 0.6);
|
||||
color: #ff4500;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 14px 18px;
|
||||
background: linear-gradient(135deg, rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.05));
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
user-select: none;
|
||||
cursor: grab;
|
||||
transition: background 0.15s ease;
|
||||
}
|
||||
|
||||
.panel-header:hover {
|
||||
background: linear-gradient(135deg, rgba(0, 0, 0, 0.15), rgba(255, 255, 255, 0.08));
|
||||
}
|
||||
|
||||
.panel-header:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.header-icon {
|
||||
font-size: 18px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-weight: 600;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.header-subtitle {
|
||||
font-size: 14px;
|
||||
opacity: 0.7;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.header-controls {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.minimize-btn {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
color: inherit;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.minimize-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.minimize-btn:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.panel-content {
|
||||
padding: 0;
|
||||
max-height: calc(100% - 60px);
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.panel-content::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.panel-content::-webkit-scrollbar-track {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.panel-content::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.info-section {
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
margin-bottom: 8px;
|
||||
border-radius: 10px;
|
||||
overflow: visible;
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.info-section:hover {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 16px 20px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
font-weight: 500;
|
||||
border-radius: 8px;
|
||||
margin: 3px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.section-header::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.05) 50%, transparent 100%);
|
||||
transform: translateX(-100%);
|
||||
transition: transform 0.6s ease;
|
||||
}
|
||||
|
||||
.section-header:hover::before {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
.section-header:hover {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
transform: translateX(2px);
|
||||
box-shadow: inset 2px 0 0 rgba(116, 185, 255, 0.5);
|
||||
}
|
||||
|
||||
.section-header:active {
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
transform: translateX(1px);
|
||||
}
|
||||
|
||||
.section-header.expanded {
|
||||
background: rgba(116, 185, 255, 0.1);
|
||||
border-left: 2px solid var(--info-panel-accent-color, #74b9ff);
|
||||
}
|
||||
|
||||
.info-section[data-section="node"] {
|
||||
background: linear-gradient(135deg, rgba(116, 185, 255, 0.05), rgba(160, 212, 104, 0.05));
|
||||
border: 1px solid rgba(116, 185, 255, 0.2);
|
||||
border-radius: 8px;
|
||||
margin: 6px;
|
||||
}
|
||||
|
||||
.info-section[data-section="node"] .section-header {
|
||||
background: linear-gradient(135deg, rgba(116, 185, 255, 0.15), rgba(160, 212, 104, 0.15));
|
||||
border-radius: 8px 8px 0 0;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.info-section[data-section="node"] .section-header:hover {
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.info-section[data-section="node"] .section-title {
|
||||
color: var(--info-panel-accent-color, #74b9ff);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.info-section[data-section="inspector"] {
|
||||
background: linear-gradient(135deg, rgba(100, 150, 255, 0.05), rgba(74, 144, 226, 0.05));
|
||||
border: 1px solid rgba(100, 150, 255, 0.2);
|
||||
border-radius: 8px;
|
||||
margin: 6px;
|
||||
}
|
||||
|
||||
.info-section[data-section="inspector"] .section-header {
|
||||
background: linear-gradient(135deg, rgba(100, 150, 255, 0.15), rgba(74, 144, 226, 0.15));
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
|
||||
.info-section[data-section="inspector"] .section-title {
|
||||
color: #6496ff;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.info-section[data-section="media"] {
|
||||
background: linear-gradient(135deg, rgba(253, 203, 110, 0.05), rgba(255, 215, 0, 0.05));
|
||||
border: 1px solid rgba(253, 203, 110, 0.2);
|
||||
border-radius: 8px;
|
||||
margin: 6px;
|
||||
}
|
||||
|
||||
.info-section[data-section="media"] .section-header {
|
||||
background: linear-gradient(135deg, rgba(253, 203, 110, 0.15), rgba(255, 215, 0, 0.15));
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
|
||||
.info-section[data-section="media"] .section-title {
|
||||
color: var(--info-panel-accent-color, #fdcb6e);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.section-icon {
|
||||
font-size: 18px;
|
||||
width: 22px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
flex: 1;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.3px;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.section-badge {
|
||||
background: rgba(100, 150, 255, 0.2);
|
||||
color: #8bb3ff;
|
||||
padding: 3px 10px;
|
||||
border-radius: 10px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.expand-icon {
|
||||
font-size: 14px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.section-header:hover .expand-icon {
|
||||
color: var(--info-panel-accent-color, #74b9ff);
|
||||
background: rgba(116, 185, 255, 0.15);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.section-header.expanded .expand-icon {
|
||||
transform: rotate(90deg) scale(1.1);
|
||||
color: var(--info-panel-accent-color, #74b9ff);
|
||||
background: rgba(116, 185, 255, 0.2);
|
||||
}
|
||||
|
||||
.section-content {
|
||||
max-height: 0;
|
||||
overflow: visible;
|
||||
transition: max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s ease;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.section-content.expanded {
|
||||
max-height: 2000px;
|
||||
opacity: 1;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.section-body {
|
||||
padding: 0 18px 16px 24px;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||
transition: all 0.2s ease;
|
||||
border-radius: 4px;
|
||||
margin: 0 -8px;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.info-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.info-row:hover {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border-bottom-color: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-weight: 500;
|
||||
opacity: 0.85;
|
||||
font-size: 15px;
|
||||
min-width: 80px;
|
||||
padding-right: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
font-size: 14px;
|
||||
color: var(--info-panel-accent-color, #a0d468);
|
||||
background: rgba(160, 212, 104, 0.12);
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
max-width: 100%;
|
||||
word-wrap: break-word;
|
||||
word-break: break-word;
|
||||
hyphens: auto;
|
||||
line-height: 1.4;
|
||||
text-align: left;
|
||||
white-space: pre-wrap;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.info-value:hover {
|
||||
background: rgba(160, 212, 104, 0.18);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.info-value.long-text {
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
max-width: 100%;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 50px 25px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.empty-state-icon {
|
||||
font-size: 36px;
|
||||
margin-bottom: 15px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.empty-state-text {
|
||||
font-size: 16px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Copy to clipboard functionality */
|
||||
.info-value[title]:hover::after {
|
||||
content: '📋';
|
||||
position: absolute;
|
||||
right: -20px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
font-size: 12px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* Smooth loading states */
|
||||
.section-body.loading {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.section-body.loading::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid transparent;
|
||||
border-top: 2px solid var(--info-panel-accent-color, #74b9ff);
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: translate(-50%, -50%) rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Enhanced scrollbar for better UX */
|
||||
.section-body::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
.section-body::-webkit-scrollbar-track {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.section-body::-webkit-scrollbar-thumb {
|
||||
background: var(--info-panel-accent-color, #74b9ff);
|
||||
border-radius: 2px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.section-body::-webkit-scrollbar-thumb:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
.pulse {
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
+11
-1
@@ -50,6 +50,8 @@
|
||||
font-size: 16px;
|
||||
line-height: 1.6;
|
||||
pointer-events: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.magnify-info-panel * {
|
||||
@@ -163,9 +165,16 @@
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
.magnify-info-panel.panel-minimized {
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
.magnify-info-panel.panel-minimized .panel-content {
|
||||
max-height: 0 !important;
|
||||
height: 0 !important;
|
||||
overflow: hidden;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
transition: max-height 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
@@ -310,7 +319,8 @@
|
||||
|
||||
.panel-content {
|
||||
padding: 0;
|
||||
max-height: calc(100% - 60px);
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
@@ -123,6 +123,7 @@ class InfoPanel {
|
||||
this.uiManager.applyStyles();
|
||||
if (this.stateManager.state.settings["🔍MagnifyGlass.InfoPanelEnabled"] && this.magnifyGlass.state.active) {
|
||||
this.uiManager.show();
|
||||
this.positionManager.positionPanel();
|
||||
} else {
|
||||
this.uiManager.hide();
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -99,6 +99,11 @@ class PositionManager {
|
||||
*/
|
||||
positionFloatingControls(controlsElement) {
|
||||
if (!controlsElement) return;
|
||||
const settings = this.stateManager.state.settings;
|
||||
if (settings["🔍MagnifyGlass.ShowHoveringControls"] === false) {
|
||||
controlsElement.style.display = "none";
|
||||
return;
|
||||
}
|
||||
const isPanelVisible = this.stateManager.state.isPanelVisible;
|
||||
const magnifyGlass = window.comfyUIMagnifyGlass;
|
||||
let referenceRect = null;
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -11,7 +11,7 @@ class StateManager {
|
||||
// Panel visibility and positioning
|
||||
isPanelVisible: false,
|
||||
wasPanelVisibleBeforeHide: false,
|
||||
isPanelMinimized: false,
|
||||
isPanelMinimized: true,
|
||||
isPanelPinned: false,
|
||||
isPanelLocked: false,
|
||||
// New lock state
|
||||
@@ -155,9 +155,9 @@ class StateManager {
|
||||
}
|
||||
setupThemeButtonListeners() {
|
||||
document.addEventListener("click", (e) => {
|
||||
var _a, _b, _c, _d, _e;
|
||||
var _a, _b, _c, _d;
|
||||
const target = e.target;
|
||||
const isThemeButton = ((_a = target.textContent) == null ? void 0 : _a.toLowerCase().includes("theme")) || ((_b = target.textContent) == null ? void 0 : _b.toLowerCase().includes("light")) || ((_c = target.textContent) == null ? void 0 : _c.toLowerCase().includes("dark")) || ((_d = target.title) == null ? void 0 : _d.toLowerCase().includes("theme")) || ((_e = target.className) == null ? void 0 : _e.toLowerCase().includes("theme"));
|
||||
const isThemeButton = ((_a = target.textContent) == null ? void 0 : _a.toLowerCase().includes("theme")) || ((_b = target.textContent) == null ? void 0 : _b.toLowerCase().includes("light")) || ((_c = target.textContent) == null ? void 0 : _c.toLowerCase().includes("dark")) || ((_d = target.title) == null ? void 0 : _d.toLowerCase().includes("theme")) || typeof target.className === "string" && target.className.toLowerCase().includes("theme");
|
||||
if (isThemeButton) {
|
||||
setTimeout(() => {
|
||||
if (this.detectCurrentTheme()) {
|
||||
|
||||
File diff suppressed because one or more lines are too long
+19
-17
@@ -43,6 +43,9 @@ class UIManager {
|
||||
this.elements.panel.appendChild(this.elements.header);
|
||||
this.elements.panel.appendChild(this.elements.content);
|
||||
this.applyStyles();
|
||||
if (this.stateManager.state.isPanelMinimized) {
|
||||
this.elements.panel.classList.add("panel-minimized");
|
||||
}
|
||||
document.body.appendChild(this.elements.panel);
|
||||
this.elements.panel.addEventListener("click", (e) => {
|
||||
const target = e.target;
|
||||
@@ -70,7 +73,7 @@ class UIManager {
|
||||
}
|
||||
}
|
||||
});
|
||||
if (this.stateManager.state.settings["🔍MagnifyGlass.ShowHoveringControls"]) {
|
||||
if (!this.elements.controls) {
|
||||
this.createFloatingControls();
|
||||
this.updateControlStates();
|
||||
}
|
||||
@@ -214,6 +217,18 @@ class UIManager {
|
||||
applyStyles() {
|
||||
if (!this.elements.panel) return;
|
||||
const settings = this.stateManager.state.settings;
|
||||
console.log(`[UIManager] applyStyles - MaxHeight: ${settings["🔍MagnifyGlass.InfoPanelMaxHeight"]}`);
|
||||
this.elements.panel.style.width = `${settings["🔍MagnifyGlass.InfoPanelWidth"]}px`;
|
||||
const maxHeight = settings["🔍MagnifyGlass.InfoPanelMaxHeight"];
|
||||
this.elements.panel.style.height = `${maxHeight}px`;
|
||||
this.elements.panel.style.maxHeight = `${maxHeight}px`;
|
||||
this.elements.panel.style.position = "absolute";
|
||||
this.elements.panel.style.zIndex = "99999";
|
||||
this.elements.panel.style.transform = "translateY(-10px)";
|
||||
this.elements.panel.style.pointerEvents = "auto";
|
||||
this.elements.panel.style.userSelect = "none";
|
||||
this.elements.panel.style.fontFamily = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif";
|
||||
this.elements.panel.style.transition = settings["🔍MagnifyGlass.InfoPanelAnimations"] ? "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)" : "none";
|
||||
const textColor = settings["🔍MagnifyGlass.InfoPanelTextColor"];
|
||||
if (textColor && typeof textColor === "string") {
|
||||
const normalizedTextColor = textColor.startsWith("#") ? textColor : `#${textColor}`;
|
||||
@@ -227,22 +242,9 @@ class UIManager {
|
||||
if (this.stateManager.state.isPanelVisible) {
|
||||
const opacityPercent = Number(settings["🔍MagnifyGlass.InfoPanelOpacity"]) || 100;
|
||||
this.elements.panel.style.opacity = (opacityPercent / 100).toString();
|
||||
} else {
|
||||
this.elements.panel.style.opacity = "0";
|
||||
}
|
||||
this.elements.panel.style.cssText = `
|
||||
position: absolute;
|
||||
width: ${settings["🔍MagnifyGlass.InfoPanelWidth"]}px;
|
||||
max-height: ${settings["🔍MagnifyGlass.InfoPanelMaxHeight"]}px;
|
||||
z-index: 99999;
|
||||
display: none;
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
transition: ${settings["🔍MagnifyGlass.InfoPanelAnimations"] ? "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)" : "none"};
|
||||
pointer-events: auto;
|
||||
user-select: none;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
${textColor && typeof textColor === "string" ? `--info-panel-text-color: ${textColor.startsWith("#") ? textColor : `#${textColor}`};` : ""}
|
||||
${accentColor && typeof accentColor === "string" ? `--info-panel-accent-color: ${accentColor.startsWith("#") ? accentColor : `#${accentColor}`};` : ""}
|
||||
`;
|
||||
}
|
||||
/**
|
||||
* Show the panel.
|
||||
@@ -321,7 +323,7 @@ class UIManager {
|
||||
buildSections(info) {
|
||||
const sections = [];
|
||||
const settings = this.stateManager.state.settings;
|
||||
if (settings["🔍MagnifyGlass.ShowInspectorTab"]) {
|
||||
if (settings["🔍MagnifyGlass.ShowInspectorTab"] && info.cursor && info.cursor.canvas) {
|
||||
const inspectorContent = [
|
||||
{ label: "Cursor Canvas", value: `(${Math.round(info.cursor.canvas.x)}, ${Math.round(info.cursor.canvas.y)})` },
|
||||
{ label: "Canvas Scale", value: `${(info.canvas.scale * 100).toFixed(1)}%` },
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -3,7 +3,7 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
|
||||
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
||||
import { isUserTyping } from "../shared/utils.js";
|
||||
import { Logger } from "../shared/logger.js";
|
||||
import { DEFAULT_PADDING } from "../shared/constants.js";
|
||||
import { DEFAULT_PADDING, DEFAULT_GLASS_Y_OFFSET } from "../shared/constants.js";
|
||||
class EventHandler {
|
||||
constructor(magnifyGlass) {
|
||||
__publicField(this, "magnifyGlass");
|
||||
@@ -156,7 +156,7 @@ class EventHandler {
|
||||
const glassSize = this.magnifyGlass.config.glassSize;
|
||||
if (!this.magnifyGlass.state.wasActivatedBefore && this.magnifyGlass.ui.glassDiv) {
|
||||
this.magnifyGlass.ui.glassDiv.style.left = `${window.innerWidth - glassSize - DEFAULT_PADDING}px`;
|
||||
this.magnifyGlass.ui.glassDiv.style.top = `${DEFAULT_PADDING}px`;
|
||||
this.magnifyGlass.ui.glassDiv.style.top = `${DEFAULT_GLASS_Y_OFFSET}px`;
|
||||
this.magnifyGlass.state.wasActivatedBefore = true;
|
||||
} else {
|
||||
this.magnifyGlass.ui.positionGlass(clientX, clientY);
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -2,7 +2,7 @@ var __defProp = Object.defineProperty;
|
||||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
||||
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
||||
import { findLiteGraphCanvas, rectsOverlap } from "../shared/utils.js";
|
||||
import { DEFAULT_PADDING } from "../shared/constants.js";
|
||||
import { DEFAULT_PADDING, DEFAULT_GLASS_Y_OFFSET } from "../shared/constants.js";
|
||||
import { registerGlassSettings } from "../shared/settings/glassSettings.js";
|
||||
import "/scripts/app.js";
|
||||
import { ConfigManager } from "./ConfigManager.js";
|
||||
@@ -285,13 +285,14 @@ class MagnifyGlass {
|
||||
*/
|
||||
resetOffsets() {
|
||||
this.config.resetOffsets();
|
||||
this.config.followCursor = false;
|
||||
if (this.state.active) {
|
||||
this.updateMagnifiedView();
|
||||
}
|
||||
if (this.ui.glassDiv) {
|
||||
const glassSize = this.config.glassSize;
|
||||
this.ui.glassDiv.style.left = `${window.innerWidth - glassSize - DEFAULT_PADDING}px`;
|
||||
this.ui.glassDiv.style.top = `${DEFAULT_PADDING}px`;
|
||||
this.ui.glassDiv.style.top = `${DEFAULT_GLASS_Y_OFFSET}px`;
|
||||
this.state.wasActivatedBefore = false;
|
||||
}
|
||||
const extensions = window.comfyUIMagnifyGlassExtensions;
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,12 +1,14 @@
|
||||
import { MagnifyGlass } from "./magnify-glass/MagnifyGlass.js";
|
||||
import { app } from "/scripts/app.js";
|
||||
import { Logger } from "./shared/logger.js";
|
||||
import { initSidebar } from "./sidebar/sidebar.js";
|
||||
app.registerExtension({
|
||||
name: "comfyui.magnify.glass",
|
||||
init() {
|
||||
const magnifyGlass = new MagnifyGlass();
|
||||
magnifyGlass.init();
|
||||
window.comfyUIMagnifyGlass = magnifyGlass;
|
||||
initSidebar();
|
||||
Logger.info("Magnify Glass extension initialized");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"magnify_glass.js","sources":["../src/magnify_glass.ts"],"sourcesContent":["/**\n * ComfyUI Magnify Glass - Main Entry Point\n * \n * Initializes the Magnify Glass extension.\n */\n\nimport { MagnifyGlass } from './magnify-glass';\nimport { Logger } from './shared/logger';\n// @ts-ignore\nimport { app } from \"/scripts/app.js\";\n\n// Global declaration handled in comfyui.d.ts\n\n// We rely on the global 'app' object provided by ComfyUI\n// Register extension\napp.registerExtension({\n name: \"comfyui.magnify.glass\",\n init() {\n // Initialize the magnify glass\n const magnifyGlass = new MagnifyGlass();\n magnifyGlass.init();\n\n // Expose to window for other extensions (like info panel) to access\n window.comfyUIMagnifyGlass = magnifyGlass;\n\n Logger.info('Magnify Glass extension initialized');\n }\n});\n"],"names":[],"mappings":";;;AAeA,IAAI,kBAAkB;AAAA,EAClB,MAAM;AAAA,EACN,OAAO;AAEH,UAAM,eAAe,IAAI,aAAA;AACzB,iBAAa,KAAA;AAGb,WAAO,sBAAsB;AAE7B,WAAO,KAAK,qCAAqC;AAAA,EACrD;AACJ,CAAC;"}
|
||||
{"version":3,"file":"magnify_glass.js","sources":["../src/magnify_glass.ts"],"sourcesContent":["/**\n * ComfyUI Magnify Glass - Main Entry Point\n * \n * Initializes the Magnify Glass extension.\n */\n\nimport { MagnifyGlass } from './magnify-glass';\nimport { Logger } from './shared/logger';\nimport { initSidebar } from './sidebar';\n// @ts-ignore\nimport { app } from \"/scripts/app.js\";\n\n// Global declaration handled in comfyui.d.ts\n\n// We rely on the global 'app' object provided by ComfyUI\n// Register extension\napp.registerExtension({\n name: \"comfyui.magnify.glass\",\n init() {\n // Initialize the magnify glass\n const magnifyGlass = new MagnifyGlass();\n magnifyGlass.init();\n\n // Expose to window for other extensions (like info panel) to access\n window.comfyUIMagnifyGlass = magnifyGlass;\n\n // Initialize sidebar\n initSidebar();\n\n Logger.info('Magnify Glass extension initialized');\n }\n});\n"],"names":[],"mappings":";;;;AAgBA,IAAI,kBAAkB;AAAA,EAClB,MAAM;AAAA,EACN,OAAO;AAEH,UAAM,eAAe,IAAI,aAAA;AACzB,iBAAa,KAAA;AAGb,WAAO,sBAAsB;AAG7B,gBAAA;AAEA,WAAO,KAAK,qCAAqC;AAAA,EACrD;AACJ,CAAC;"}
|
||||
@@ -23,13 +23,14 @@ const ACTIVATION_KEYS = ["x", "z", "m", "q", "v", "c"];
|
||||
const RESET_KEYS = ["r", "o", "p", "k", "l"];
|
||||
const TOGGLE_FOLLOW_KEYS = ["f", "g", "h", "j", "k"];
|
||||
const DEFAULT_PADDING = 20;
|
||||
const DEFAULT_GLASS_Y_OFFSET = 120;
|
||||
const DEFAULT_GLASS_SIZE = 300;
|
||||
const DEFAULT_ZOOM_FACTOR = 300;
|
||||
const DEFAULT_BORDER_WIDTH = 1;
|
||||
const DEFAULT_BORDER_COLOR = "#6b7280";
|
||||
const DEFAULT_OFFSET_STEP = 5;
|
||||
const DEFAULT_PANEL_WIDTH = 320;
|
||||
const DEFAULT_PANEL_MAX_HEIGHT = 1e3;
|
||||
const DEFAULT_PANEL_WIDTH = 300;
|
||||
const DEFAULT_PANEL_MAX_HEIGHT = 300;
|
||||
const DEFAULT_PANEL_OPACITY = 100;
|
||||
const Z_INDEX = {
|
||||
GLASS: 98999,
|
||||
@@ -44,6 +45,7 @@ export {
|
||||
DEFAULT_BORDER_COLOR,
|
||||
DEFAULT_BORDER_WIDTH,
|
||||
DEFAULT_GLASS_SIZE,
|
||||
DEFAULT_GLASS_Y_OFFSET,
|
||||
DEFAULT_OFFSET_STEP,
|
||||
DEFAULT_PADDING,
|
||||
DEFAULT_PANEL_MAX_HEIGHT,
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"constants.js","sources":["../../src/shared/constants.ts"],"sourcesContent":["/**\n * ComfyUI MagnifyGlass - Shared Constants\n * \n * Configuration constants shared between magnify glass and info panel modules.\n */\n\n// Glass positioning options\nexport const GLASS_POSITIONS = [\n \"Top\",\n \"Bottom\",\n \"Left\",\n \"Right\",\n \"Top-Left\",\n \"Top-Right\",\n \"Bottom-Left\",\n \"Bottom-Right\"\n] as const;\n\nexport type GlassPosition = typeof GLASS_POSITIONS[number];\n\n// Glass shape options\nexport const GLASS_SHAPES = [\n \"Circle\",\n \"Square\",\n \"Rounded Square\"\n] as const;\n\nexport type GlassShape = typeof GLASS_SHAPES[number];\n\n// Texture filtering modes\nexport const TEXTURE_FILTERS = [\n \"Linear\",\n \"Nearest\"\n] as const;\n\nexport type TextureFilter = typeof TEXTURE_FILTERS[number];\n\n// Info panel positions relative to glass\nexport const PANEL_POSITIONS = [\n \"Top\",\n \"Bottom\",\n \"Left\",\n \"Right\"\n] as const;\n\nexport type PanelPosition = typeof PANEL_POSITIONS[number];\n\n// Available keys for settings\nexport const ACTIVATION_KEYS = [\"x\", \"z\", \"m\", \"q\", \"v\", \"c\"] as const;\nexport const RESET_KEYS = [\"r\", \"o\", \"p\", \"k\", \"l\"] as const;\nexport const TOGGLE_FOLLOW_KEYS = [\"f\", \"g\", \"h\", \"j\", \"k\"] as const;\n\n// UI Constants\nexport const DEFAULT_PADDING = 20;\nexport const DEFAULT_GLASS_SIZE = 300;\nexport const DEFAULT_ZOOM_FACTOR = 300; // 3x as percentage\nexport const DEFAULT_BORDER_WIDTH = 1;\nexport const DEFAULT_BORDER_COLOR = \"#6b7280\";\nexport const DEFAULT_OFFSET_STEP = 5;\n\n// Panel Constants\nexport const DEFAULT_PANEL_WIDTH = 320;\nexport const DEFAULT_PANEL_MAX_HEIGHT = 1000;\nexport const DEFAULT_PANEL_OPACITY = 100;\n\n// Z-Index hierarchy\nexport const Z_INDEX = {\n GLASS: 98999,\n DEBUG: 99000,\n PANEL: 99999,\n CONTROLS: 99998\n} as const;\n\n// Animation durations (ms)\nexport const ANIMATION = {\n PANEL_TRANSITION: 300,\n AUTO_COLLAPSE_DELAY: 1500,\n THEME_DETECTION_INTERVAL: 2000\n} as const;\n\n// LocalStorage keys\nexport const STORAGE_KEYS = {\n OFFSET_X: 'comfyui_magnify_offset_x',\n OFFSET_Y: 'comfyui_magnify_offset_y'\n} as const;\n"],"names":[],"mappings":"AAOO,MAAM,kBAAkB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ;AAKO,MAAM,eAAe;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AACJ;AAaO,MAAM,kBAAkB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ;AAKO,MAAM,kBAAkB,CAAC,KAAK,KAAK,KAAK,KAAK,KAAK,GAAG;AACrD,MAAM,aAAa,CAAC,KAAK,KAAK,KAAK,KAAK,GAAG;AAC3C,MAAM,qBAAqB,CAAC,KAAK,KAAK,KAAK,KAAK,GAAG;AAGnD,MAAM,kBAAkB;AACxB,MAAM,qBAAqB;AAC3B,MAAM,sBAAsB;AAC5B,MAAM,uBAAuB;AAC7B,MAAM,uBAAuB;AAC7B,MAAM,sBAAsB;AAG5B,MAAM,sBAAsB;AAC5B,MAAM,2BAA2B;AACjC,MAAM,wBAAwB;AAG9B,MAAM,UAAU;AAAA,EACnB,OAAO;AAAA,EACP,OAAO;AAGX;AAUO,MAAM,eAAe;AAAA,EACxB,UAAU;AAAA,EACV,UAAU;AACd;"}
|
||||
{"version":3,"file":"constants.js","sources":["../../src/shared/constants.ts"],"sourcesContent":["/**\n * ComfyUI MagnifyGlass - Shared Constants\n * \n * Configuration constants shared between magnify glass and info panel modules.\n */\n\n// Glass positioning options\nexport const GLASS_POSITIONS = [\n \"Top\",\n \"Bottom\",\n \"Left\",\n \"Right\",\n \"Top-Left\",\n \"Top-Right\",\n \"Bottom-Left\",\n \"Bottom-Right\"\n] as const;\n\nexport type GlassPosition = typeof GLASS_POSITIONS[number];\n\n// Glass shape options\nexport const GLASS_SHAPES = [\n \"Circle\",\n \"Square\",\n \"Rounded Square\"\n] as const;\n\nexport type GlassShape = typeof GLASS_SHAPES[number];\n\n// Texture filtering modes\nexport const TEXTURE_FILTERS = [\n \"Linear\",\n \"Nearest\"\n] as const;\n\nexport type TextureFilter = typeof TEXTURE_FILTERS[number];\n\n// Info panel positions relative to glass\nexport const PANEL_POSITIONS = [\n \"Top\",\n \"Bottom\",\n \"Left\",\n \"Right\"\n] as const;\n\nexport type PanelPosition = typeof PANEL_POSITIONS[number];\n\n// Available keys for settings\nexport const ACTIVATION_KEYS = [\"x\", \"z\", \"m\", \"q\", \"v\", \"c\"] as const;\nexport const RESET_KEYS = [\"r\", \"o\", \"p\", \"k\", \"l\"] as const;\nexport const TOGGLE_FOLLOW_KEYS = [\"f\", \"g\", \"h\", \"j\", \"k\"] as const;\n\n// UI Constants\nexport const DEFAULT_PADDING = 20;\nexport const DEFAULT_GLASS_Y_OFFSET = 120; // Starting Y position offset (below top bar)\nexport const DEFAULT_GLASS_SIZE = 300;\nexport const DEFAULT_ZOOM_FACTOR = 300; // 3x as percentage\nexport const DEFAULT_BORDER_WIDTH = 1;\nexport const DEFAULT_BORDER_COLOR = \"#6b7280\";\nexport const DEFAULT_OFFSET_STEP = 5;\n\n// Panel Constants\nexport const DEFAULT_PANEL_WIDTH = 300;\nexport const DEFAULT_PANEL_MAX_HEIGHT = 300;\nexport const DEFAULT_PANEL_OPACITY = 100;\n\n// Z-Index hierarchy\nexport const Z_INDEX = {\n GLASS: 98999,\n DEBUG: 99000,\n PANEL: 99999,\n CONTROLS: 99998\n} as const;\n\n// Animation durations (ms)\nexport const ANIMATION = {\n PANEL_TRANSITION: 300,\n AUTO_COLLAPSE_DELAY: 1500,\n THEME_DETECTION_INTERVAL: 2000\n} as const;\n\n// LocalStorage keys\nexport const STORAGE_KEYS = {\n OFFSET_X: 'comfyui_magnify_offset_x',\n OFFSET_Y: 'comfyui_magnify_offset_y'\n} as const;\n"],"names":[],"mappings":"AAOO,MAAM,kBAAkB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ;AAKO,MAAM,eAAe;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AACJ;AAaO,MAAM,kBAAkB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ;AAKO,MAAM,kBAAkB,CAAC,KAAK,KAAK,KAAK,KAAK,KAAK,GAAG;AACrD,MAAM,aAAa,CAAC,KAAK,KAAK,KAAK,KAAK,GAAG;AAC3C,MAAM,qBAAqB,CAAC,KAAK,KAAK,KAAK,KAAK,GAAG;AAGnD,MAAM,kBAAkB;AACxB,MAAM,yBAAyB;AAC/B,MAAM,qBAAqB;AAC3B,MAAM,sBAAsB;AAC5B,MAAM,uBAAuB;AAC7B,MAAM,uBAAuB;AAC7B,MAAM,sBAAsB;AAG5B,MAAM,sBAAsB;AAC5B,MAAM,2BAA2B;AACjC,MAAM,wBAAwB;AAG9B,MAAM,UAAU;AAAA,EACnB,OAAO;AAAA,EACP,OAAO;AAGX;AAUO,MAAM,eAAe;AAAA,EACxB,UAAU;AAAA,EACV,UAAU;AACd;"}
|
||||
@@ -250,6 +250,77 @@ function registerGlassSettings(magnifyGlass) {
|
||||
}
|
||||
}
|
||||
});
|
||||
app.ui.settings.addSetting({
|
||||
id: "🔍MagnifyGlass.Action.ResetPosition",
|
||||
name: "🔄 Magnify Glass: Reset Position",
|
||||
type: "boolean",
|
||||
defaultValue: false,
|
||||
tooltip: "Toggle to reset the glass position and disable follow cursor.",
|
||||
onChange: (value) => {
|
||||
if (value) {
|
||||
if (magnifyGlass == null ? void 0 : magnifyGlass.resetOffsets) {
|
||||
magnifyGlass.resetOffsets();
|
||||
}
|
||||
setTimeout(() => {
|
||||
try {
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.Action.ResetPosition", false);
|
||||
} catch (e) {
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
});
|
||||
app.ui.settings.addSetting({
|
||||
id: "🔍MagnifyGlass.Action.ResetAll",
|
||||
name: "⚠️ Magnify Glass: Reset ALL Settings",
|
||||
type: "boolean",
|
||||
defaultValue: false,
|
||||
tooltip: "Toggle to reset ALL settings to defaults.",
|
||||
onChange: (value) => {
|
||||
if (value) {
|
||||
if (confirm("Reset ALL Magnify Glass settings?")) {
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.ZoomFactor", 300);
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.GlassSize", 250);
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.GlassShape", "Rounded Square");
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.GlassPosition", "Top-Right");
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.TextureFiltering", "Linear");
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.BorderWidth", 2);
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.BorderColor", "#ffffff");
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.BorderEnabled", true);
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.FollowCursor", false);
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.AlwaysActiveMode", true);
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.ActivationKey", "x");
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.ResetKey", "o");
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.ToggleFollowCursorKey", "h");
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.AltRequired", false);
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.OffsetStep", 10);
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.DebugMode", false);
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.InfoPanelEnabled", true);
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.InfoPanelPosition", "Bottom");
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.InfoPanelWidth", 300);
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.InfoPanelMaxHeight", 300);
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.InfoPanelOpacity", 95);
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.InfoPanelTextColor", "#6b7280");
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.InfoPanelAccentColor", "#3b82f6");
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.InfoPanelAnimations", false);
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.ShowHoveringControls", true);
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.ControlsPosition", "bottom");
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.ToggleHotkey", "i");
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.GlassPreviewToggleHotkey", "g");
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.PinPanelHotkey", "u");
|
||||
if (magnifyGlass == null ? void 0 : magnifyGlass.resetOffsets) {
|
||||
magnifyGlass.resetOffsets();
|
||||
}
|
||||
}
|
||||
setTimeout(() => {
|
||||
try {
|
||||
app.ui.settings.setSettingValue("🔍MagnifyGlass.Action.ResetAll", false);
|
||||
} catch (e) {
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
export {
|
||||
registerGlassSettings
|
||||
|
||||
File diff suppressed because one or more lines are too long
+352
@@ -0,0 +1,352 @@
|
||||
/**
|
||||
* ComfyUI MagnifyGlass - Sidebar CSS
|
||||
*
|
||||
* Styles for the sidebar settings and inspector panels.
|
||||
*/
|
||||
|
||||
.magnify-sidebar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
font-size: 13px;
|
||||
color: var(--fg-color, #ddd);
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.magnify-sidebar-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid var(--border-color, #333);
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.magnify-sidebar-header svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.magnify-sidebar-header h2 {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.magnify-sidebar-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
/* Section styling */
|
||||
.magnify-sidebar-section {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.magnify-sidebar-section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 16px;
|
||||
cursor: pointer;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border-bottom: 1px solid var(--border-color, #333);
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.magnify-sidebar-section-header:hover {
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
.magnify-sidebar-section-header svg {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.magnify-sidebar-section-header.collapsed svg {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.magnify-sidebar-section-header span {
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.magnify-sidebar-section-body {
|
||||
padding: 12px 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.magnify-sidebar-section-body.collapsed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Control row */
|
||||
.magnify-control-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.magnify-control-row label {
|
||||
font-size: 11px;
|
||||
color: var(--descrip-text, #999);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.magnify-control-row .control-value {
|
||||
font-size: 11px;
|
||||
color: var(--fg-color, #ddd);
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.magnify-control-label-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Slider styling */
|
||||
.magnify-slider {
|
||||
width: 100%;
|
||||
height: 6px;
|
||||
border-radius: 3px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
appearance: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.magnify-slider::-webkit-slider-thumb {
|
||||
appearance: none;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 50%;
|
||||
background: var(--p-primary-color, #3b82f6);
|
||||
cursor: pointer;
|
||||
transition: transform 0.1s;
|
||||
}
|
||||
|
||||
.magnify-slider::-webkit-slider-thumb:hover {
|
||||
transform: scale(1.15);
|
||||
}
|
||||
|
||||
.magnify-slider::-moz-range-thumb {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 50%;
|
||||
background: var(--p-primary-color, #3b82f6);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Toggle styling */
|
||||
.magnify-toggle-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
.magnify-toggle-row label {
|
||||
font-size: 12px;
|
||||
color: var(--fg-color, #ddd);
|
||||
}
|
||||
|
||||
.magnify-toggle {
|
||||
position: relative;
|
||||
width: 36px;
|
||||
height: 20px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.magnify-toggle.active {
|
||||
background: var(--p-primary-color, #3b82f6);
|
||||
}
|
||||
|
||||
.magnify-toggle::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: white;
|
||||
border-radius: 50%;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.magnify-toggle.active::after {
|
||||
transform: translateX(16px);
|
||||
}
|
||||
|
||||
/* Select styling */
|
||||
.magnify-select {
|
||||
width: 100%;
|
||||
padding: 6px 10px;
|
||||
border: 1px solid var(--border-color, #444);
|
||||
border-radius: 4px;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
color: var(--fg-color, #ddd);
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
appearance: none;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23999' d='M6 8L2 4h8z'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 8px center;
|
||||
}
|
||||
|
||||
.magnify-select:focus {
|
||||
outline: none;
|
||||
border-color: var(--p-primary-color, #3b82f6);
|
||||
}
|
||||
|
||||
/* Inspector styles */
|
||||
.magnify-inspector-section {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.magnify-inspector-group {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.magnify-inspector-group:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.magnify-inspector-group-title {
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
color: var(--descrip-text, #888);
|
||||
margin-bottom: 8px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.magnify-inspector-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 4px 0;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.magnify-inspector-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.magnify-inspector-label {
|
||||
color: var(--descrip-text, #999);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.magnify-inspector-value {
|
||||
color: var(--fg-color, #ddd);
|
||||
font-size: 12px;
|
||||
font-family: monospace;
|
||||
text-align: right;
|
||||
max-width: 60%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.magnify-inspector-empty {
|
||||
color: var(--descrip-text, #666);
|
||||
font-style: italic;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
/* Node badge */
|
||||
.magnify-node-badge {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
background: var(--p-primary-color, #3b82f6);
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/* Button styling */
|
||||
.magnify-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
padding: 8px 12px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
background: var(--p-primary-color, #3b82f6);
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s, transform 0.1s;
|
||||
}
|
||||
|
||||
.magnify-btn:hover {
|
||||
background: var(--p-primary-hover-color, #2563eb);
|
||||
}
|
||||
|
||||
.magnify-btn:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.magnify-btn-secondary {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.magnify-btn-secondary:hover {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
/* Color picker styling */
|
||||
.magnify-color-row {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.magnify-color-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.magnify-color-input {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
padding: 0;
|
||||
border: 1px solid var(--border-color, #444);
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.magnify-color-input::-webkit-color-swatch-wrapper {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.magnify-color-input::-webkit-color-swatch {
|
||||
border-radius: 2px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.magnify-color-preview {
|
||||
font-size: 11px;
|
||||
font-family: monospace;
|
||||
color: var(--descrip-text, #999);
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
import { app } from "/scripts/app.js";
|
||||
const Icons = {
|
||||
chevronDown: `<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>`
|
||||
};
|
||||
let inspectorBody = null;
|
||||
let updateInterval = null;
|
||||
function formatNumber(value, decimals = 0) {
|
||||
return value.toFixed(decimals);
|
||||
}
|
||||
function getHoveredNode() {
|
||||
try {
|
||||
const graph = app.graph;
|
||||
if (!graph || !graph.canvas) return null;
|
||||
const canvas = graph.canvas;
|
||||
const mousePos = canvas.graph_mouse;
|
||||
if (!mousePos) return null;
|
||||
const nodes = graph._nodes;
|
||||
for (let i = nodes.length - 1; i >= 0; i--) {
|
||||
const node = nodes[i];
|
||||
if (node.pos && node.size) {
|
||||
const [x, y] = node.pos;
|
||||
const [w, h] = node.size;
|
||||
if (mousePos[0] >= x && mousePos[0] <= x + w && mousePos[1] >= y && mousePos[1] <= y + h) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
function getCanvasInfo() {
|
||||
var _a;
|
||||
try {
|
||||
const graph = app.graph;
|
||||
if (!graph || !graph.canvas) {
|
||||
return { scale: 1, cursor: { x: 0, y: 0 } };
|
||||
}
|
||||
const canvas = graph.canvas;
|
||||
const scale = ((_a = canvas.ds) == null ? void 0 : _a.scale) || 1;
|
||||
const mousePos = canvas.graph_mouse || [0, 0];
|
||||
return {
|
||||
scale: scale * 100,
|
||||
cursor: { x: mousePos[0] || 0, y: mousePos[1] || 0 }
|
||||
};
|
||||
} catch {
|
||||
return { scale: 1, cursor: { x: 0, y: 0 } };
|
||||
}
|
||||
}
|
||||
function createRow(label, value) {
|
||||
const row = document.createElement("div");
|
||||
row.className = "magnify-inspector-row";
|
||||
const labelEl = document.createElement("span");
|
||||
labelEl.className = "magnify-inspector-label";
|
||||
labelEl.textContent = label;
|
||||
const valueEl = document.createElement("span");
|
||||
valueEl.className = "magnify-inspector-value";
|
||||
valueEl.textContent = value;
|
||||
valueEl.title = value;
|
||||
row.appendChild(labelEl);
|
||||
row.appendChild(valueEl);
|
||||
return row;
|
||||
}
|
||||
function createGroup(title) {
|
||||
const group = document.createElement("div");
|
||||
group.className = "magnify-inspector-group";
|
||||
const titleEl = document.createElement("div");
|
||||
titleEl.className = "magnify-inspector-group-title";
|
||||
titleEl.textContent = title;
|
||||
group.appendChild(titleEl);
|
||||
return group;
|
||||
}
|
||||
function updateInspector() {
|
||||
var _a, _b;
|
||||
if (!inspectorBody) return;
|
||||
const canvasInfo = getCanvasInfo();
|
||||
const hoveredNode = getHoveredNode();
|
||||
inspectorBody.innerHTML = "";
|
||||
const canvasGroup = createGroup("Canvas");
|
||||
canvasGroup.appendChild(createRow("Cursor", `(${formatNumber(canvasInfo.cursor.x)}, ${formatNumber(canvasInfo.cursor.y)})`));
|
||||
canvasGroup.appendChild(createRow("Scale", `${formatNumber(canvasInfo.scale, 1)}%`));
|
||||
inspectorBody.appendChild(canvasGroup);
|
||||
const magnifyGlass = window.comfyUIMagnifyGlass;
|
||||
if (magnifyGlass) {
|
||||
const glassGroup = createGroup("Magnify Glass");
|
||||
glassGroup.appendChild(createRow("Zoom", `${formatNumber((((_a = magnifyGlass.config) == null ? void 0 : _a.zoomFactor) || 1) * 100)}%`));
|
||||
glassGroup.appendChild(createRow("Active", ((_b = magnifyGlass.state) == null ? void 0 : _b.active) ? "Yes" : "No"));
|
||||
inspectorBody.appendChild(glassGroup);
|
||||
}
|
||||
if (hoveredNode) {
|
||||
const nodeGroup = createGroup("Hovered Node");
|
||||
const badge = document.createElement("div");
|
||||
badge.className = "magnify-node-badge";
|
||||
badge.textContent = hoveredNode.type || "Unknown";
|
||||
nodeGroup.appendChild(badge);
|
||||
nodeGroup.appendChild(createRow("Title", hoveredNode.title || "Untitled"));
|
||||
nodeGroup.appendChild(createRow("ID", String(hoveredNode.id)));
|
||||
if (hoveredNode.pos) {
|
||||
nodeGroup.appendChild(createRow("Position", `(${formatNumber(hoveredNode.pos[0])}, ${formatNumber(hoveredNode.pos[1])})`));
|
||||
}
|
||||
if (hoveredNode.size) {
|
||||
nodeGroup.appendChild(createRow("Size", `${formatNumber(hoveredNode.size[0])} × ${formatNumber(hoveredNode.size[1])}`));
|
||||
}
|
||||
if (hoveredNode.widgets && hoveredNode.widgets.length > 0) {
|
||||
const widgetGroup = createGroup("Parameters");
|
||||
const widgetsToShow = hoveredNode.widgets.slice(0, 6);
|
||||
for (const widget of widgetsToShow) {
|
||||
if (widget.name && widget.value !== void 0) {
|
||||
let value = String(widget.value);
|
||||
if (value.length > 30) value = value.substring(0, 27) + "...";
|
||||
widgetGroup.appendChild(createRow(widget.name, value));
|
||||
}
|
||||
}
|
||||
if (hoveredNode.widgets.length > 6) {
|
||||
const moreEl = document.createElement("div");
|
||||
moreEl.style.cssText = "font-size: 11px; color: #666; padding: 4px 0;";
|
||||
moreEl.textContent = `+${hoveredNode.widgets.length - 6} more...`;
|
||||
widgetGroup.appendChild(moreEl);
|
||||
}
|
||||
inspectorBody.appendChild(widgetGroup);
|
||||
}
|
||||
inspectorBody.appendChild(nodeGroup);
|
||||
} else {
|
||||
const empty = document.createElement("div");
|
||||
empty.className = "magnify-inspector-empty";
|
||||
empty.textContent = "Hover over a node to inspect";
|
||||
inspectorBody.appendChild(empty);
|
||||
}
|
||||
}
|
||||
function renderInspectorPanel(container) {
|
||||
const section = document.createElement("div");
|
||||
section.className = "magnify-sidebar-section";
|
||||
const header = document.createElement("div");
|
||||
header.className = "magnify-sidebar-section-header";
|
||||
header.innerHTML = `${Icons.chevronDown}<span>Inspector</span>`;
|
||||
const body = document.createElement("div");
|
||||
body.className = "magnify-sidebar-section-body magnify-inspector-section";
|
||||
inspectorBody = body;
|
||||
header.addEventListener("click", () => {
|
||||
header.classList.toggle("collapsed");
|
||||
body.classList.toggle("collapsed");
|
||||
});
|
||||
section.appendChild(header);
|
||||
section.appendChild(body);
|
||||
container.appendChild(section);
|
||||
updateInspector();
|
||||
if (updateInterval) {
|
||||
clearInterval(updateInterval);
|
||||
}
|
||||
updateInterval = window.setInterval(updateInspector, 100);
|
||||
}
|
||||
function cleanupInspector() {
|
||||
if (updateInterval) {
|
||||
clearInterval(updateInterval);
|
||||
updateInterval = null;
|
||||
}
|
||||
inspectorBody = null;
|
||||
}
|
||||
export {
|
||||
cleanupInspector,
|
||||
renderInspectorPanel
|
||||
};
|
||||
//# sourceMappingURL=SidebarInspector.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,552 @@
|
||||
import { app } from "/scripts/app.js";
|
||||
import { GLASS_SHAPES, GLASS_POSITIONS, ACTIVATION_KEYS, RESET_KEYS, TOGGLE_FOLLOW_KEYS, PANEL_POSITIONS } from "../shared/constants.js";
|
||||
const Icons = {
|
||||
chevronDown: `<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>`,
|
||||
reset: `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="1 4 1 10 7 10"></polyline><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"></path></svg>`
|
||||
};
|
||||
function createSlider(label, value, min, max, step, unit, onChange, tooltip, onInput) {
|
||||
const row = document.createElement("div");
|
||||
row.className = "magnify-control-row";
|
||||
if (tooltip) row.title = tooltip;
|
||||
const labelRow = document.createElement("div");
|
||||
labelRow.className = "magnify-control-label-row";
|
||||
const labelEl = document.createElement("label");
|
||||
labelEl.textContent = label;
|
||||
const valueEl = document.createElement("span");
|
||||
valueEl.className = "control-value";
|
||||
valueEl.textContent = `${value}${unit}`;
|
||||
labelRow.appendChild(labelEl);
|
||||
labelRow.appendChild(valueEl);
|
||||
const slider = document.createElement("input");
|
||||
slider.type = "range";
|
||||
slider.className = "magnify-slider";
|
||||
slider.min = String(min);
|
||||
slider.max = String(max);
|
||||
slider.step = String(step);
|
||||
slider.value = String(value);
|
||||
slider.addEventListener("mousedown", (e) => e.stopPropagation());
|
||||
slider.addEventListener("touchstart", (e) => e.stopPropagation());
|
||||
slider.addEventListener("pointerdown", (e) => e.stopPropagation());
|
||||
slider.addEventListener("input", (e) => {
|
||||
e.stopPropagation();
|
||||
const newValue = parseFloat(slider.value);
|
||||
valueEl.textContent = `${newValue}${unit}`;
|
||||
if (onInput) onInput(newValue);
|
||||
});
|
||||
slider.addEventListener("change", (e) => {
|
||||
e.stopPropagation();
|
||||
const newValue = parseFloat(slider.value);
|
||||
onChange(newValue);
|
||||
});
|
||||
row.appendChild(labelRow);
|
||||
row.appendChild(slider);
|
||||
return row;
|
||||
}
|
||||
function createToggle(label, checked, onChange, tooltip) {
|
||||
const row = document.createElement("div");
|
||||
row.className = "magnify-toggle-row";
|
||||
if (tooltip) row.title = tooltip;
|
||||
const labelEl = document.createElement("label");
|
||||
labelEl.textContent = label;
|
||||
const toggle = document.createElement("div");
|
||||
toggle.className = `magnify-toggle${checked ? " active" : ""}`;
|
||||
toggle.addEventListener("click", () => {
|
||||
const isActive = toggle.classList.toggle("active");
|
||||
onChange(isActive);
|
||||
});
|
||||
row.appendChild(labelEl);
|
||||
row.appendChild(toggle);
|
||||
return row;
|
||||
}
|
||||
function createSelect(label, value, options, onChange, tooltip) {
|
||||
const row = document.createElement("div");
|
||||
row.className = "magnify-control-row";
|
||||
if (tooltip) row.title = tooltip;
|
||||
const labelEl = document.createElement("label");
|
||||
labelEl.textContent = label;
|
||||
const select = document.createElement("select");
|
||||
select.className = "magnify-select";
|
||||
options.forEach((opt) => {
|
||||
const option = document.createElement("option");
|
||||
option.value = opt;
|
||||
option.textContent = opt;
|
||||
if (opt === value) option.selected = true;
|
||||
select.appendChild(option);
|
||||
});
|
||||
select.addEventListener("change", () => onChange(select.value));
|
||||
row.appendChild(labelEl);
|
||||
row.appendChild(select);
|
||||
return row;
|
||||
}
|
||||
function createColorPicker(label, value, onChange, tooltip, onInput) {
|
||||
const row = document.createElement("div");
|
||||
row.className = "magnify-control-row magnify-color-row";
|
||||
if (tooltip) row.title = tooltip;
|
||||
const labelEl = document.createElement("label");
|
||||
labelEl.textContent = label;
|
||||
const colorWrapper = document.createElement("div");
|
||||
colorWrapper.className = "magnify-color-wrapper";
|
||||
const colorInput = document.createElement("input");
|
||||
colorInput.type = "color";
|
||||
colorInput.className = "magnify-color-input";
|
||||
colorInput.value = value;
|
||||
const colorPreview = document.createElement("span");
|
||||
colorPreview.className = "magnify-color-preview";
|
||||
colorPreview.textContent = value;
|
||||
colorInput.addEventListener("input", () => {
|
||||
colorPreview.textContent = colorInput.value;
|
||||
if (onInput) onInput(colorInput.value);
|
||||
});
|
||||
colorInput.addEventListener("change", () => {
|
||||
onChange(colorInput.value);
|
||||
});
|
||||
colorWrapper.appendChild(colorInput);
|
||||
colorWrapper.appendChild(colorPreview);
|
||||
row.appendChild(labelEl);
|
||||
row.appendChild(colorWrapper);
|
||||
return row;
|
||||
}
|
||||
function getSettingValue(id, defaultValue) {
|
||||
try {
|
||||
const value = app.ui.settings.getSettingValue(id);
|
||||
return value !== void 0 ? value : defaultValue;
|
||||
} catch {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
function setSettingValue(id, value) {
|
||||
try {
|
||||
app.ui.settings.setSettingValue(id, value);
|
||||
} catch (e) {
|
||||
console.warn(`Failed to set setting ${id}:`, e);
|
||||
}
|
||||
}
|
||||
const SECTION_STATE_KEY = "magnifyglass-sidebar-sections";
|
||||
function getSectionStates() {
|
||||
try {
|
||||
const saved = localStorage.getItem(SECTION_STATE_KEY);
|
||||
return saved ? JSON.parse(saved) : {};
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
function saveSectionState(title, collapsed) {
|
||||
try {
|
||||
const states = getSectionStates();
|
||||
states[title] = collapsed;
|
||||
localStorage.setItem(SECTION_STATE_KEY, JSON.stringify(states));
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
function createSection(title, defaultCollapsed = false) {
|
||||
const section = document.createElement("div");
|
||||
section.className = "magnify-sidebar-section";
|
||||
const savedStates = getSectionStates();
|
||||
const collapsed = savedStates[title] !== void 0 ? savedStates[title] : defaultCollapsed;
|
||||
const header = document.createElement("div");
|
||||
header.className = `magnify-sidebar-section-header${collapsed ? " collapsed" : ""}`;
|
||||
header.innerHTML = `${Icons.chevronDown}<span>${title}</span>`;
|
||||
const body = document.createElement("div");
|
||||
body.className = `magnify-sidebar-section-body${collapsed ? " collapsed" : ""}`;
|
||||
header.addEventListener("click", () => {
|
||||
const isCollapsed = header.classList.toggle("collapsed");
|
||||
body.classList.toggle("collapsed");
|
||||
saveSectionState(title, isCollapsed);
|
||||
});
|
||||
section.appendChild(header);
|
||||
section.appendChild(body);
|
||||
return { section, body };
|
||||
}
|
||||
function renderSettingsPanel(container) {
|
||||
const activationKey = getSettingValue("🔍MagnifyGlass.ActivationKey", "x").toUpperCase();
|
||||
const resetKey = getSettingValue("🔍MagnifyGlass.ResetKey", "o").toUpperCase();
|
||||
const toggleFollowKey = getSettingValue("🔍MagnifyGlass.ToggleFollowCursorKey", "h").toUpperCase();
|
||||
const glassSection = createSection("Glass Settings");
|
||||
glassSection.body.appendChild(createSlider(
|
||||
"Zoom Factor",
|
||||
getSettingValue("🔍MagnifyGlass.ZoomFactor", 300),
|
||||
100,
|
||||
1e3,
|
||||
25,
|
||||
"%",
|
||||
(value) => setSettingValue("🔍MagnifyGlass.ZoomFactor", value),
|
||||
"Magnification level of the glass (100-1000%)",
|
||||
(value) => {
|
||||
const mg = window.comfyUIMagnifyGlass;
|
||||
if (mg == null ? void 0 : mg.config) {
|
||||
mg.config.zoomFactor = value / 100;
|
||||
if (mg.state.active) mg.updateMagnifiedView();
|
||||
}
|
||||
}
|
||||
));
|
||||
glassSection.body.appendChild(createSlider(
|
||||
"Glass Size",
|
||||
getSettingValue("🔍MagnifyGlass.GlassSize", 250),
|
||||
50,
|
||||
500,
|
||||
10,
|
||||
"px",
|
||||
(value) => setSettingValue("🔍MagnifyGlass.GlassSize", value),
|
||||
"Size of the magnifying glass in pixels",
|
||||
(value) => {
|
||||
const mg = window.comfyUIMagnifyGlass;
|
||||
if (mg == null ? void 0 : mg.config) {
|
||||
mg.config.glassSize = value;
|
||||
mg.applyUiChanges();
|
||||
if (mg.state.active) mg.updateMagnifiedView();
|
||||
}
|
||||
}
|
||||
));
|
||||
glassSection.body.appendChild(createSelect(
|
||||
"Shape",
|
||||
getSettingValue("🔍MagnifyGlass.GlassShape", "Rounded Square"),
|
||||
GLASS_SHAPES,
|
||||
(value) => setSettingValue("🔍MagnifyGlass.GlassShape", value),
|
||||
"Shape of the magnifying glass"
|
||||
));
|
||||
glassSection.body.appendChild(createSelect(
|
||||
"Position",
|
||||
getSettingValue("🔍MagnifyGlass.GlassPosition", "Top-Right"),
|
||||
GLASS_POSITIONS,
|
||||
(value) => setSettingValue("🔍MagnifyGlass.GlassPosition", value),
|
||||
`Default position of the glass. Press ${resetKey} to reset.`
|
||||
));
|
||||
glassSection.body.appendChild(createSelect(
|
||||
"Filtering",
|
||||
getSettingValue("🔍MagnifyGlass.TextureFiltering", "Linear"),
|
||||
["Linear", "Nearest"],
|
||||
(value) => setSettingValue("🔍MagnifyGlass.TextureFiltering", value),
|
||||
"Texture filtering: Linear (smooth) or Nearest (pixelated)"
|
||||
));
|
||||
glassSection.body.appendChild(createSlider(
|
||||
"Border Width",
|
||||
getSettingValue("🔍MagnifyGlass.BorderWidth", 2),
|
||||
0,
|
||||
10,
|
||||
0.5,
|
||||
"px",
|
||||
(value) => setSettingValue("🔍MagnifyGlass.BorderWidth", value),
|
||||
"Width of the glass border in pixels",
|
||||
(value) => {
|
||||
const mg = window.comfyUIMagnifyGlass;
|
||||
if (mg == null ? void 0 : mg.config) {
|
||||
mg.config.borderWidth = value;
|
||||
mg.applyUiChanges();
|
||||
}
|
||||
}
|
||||
));
|
||||
glassSection.body.appendChild(createColorPicker(
|
||||
"Border Color",
|
||||
getSettingValue("🔍MagnifyGlass.BorderColor", "#ffffff"),
|
||||
(value) => setSettingValue("🔍MagnifyGlass.BorderColor", value),
|
||||
"Color of the glass border",
|
||||
(value) => {
|
||||
const mg = window.comfyUIMagnifyGlass;
|
||||
if (mg == null ? void 0 : mg.config) {
|
||||
mg.config.borderColor = value;
|
||||
mg.applyUiChanges();
|
||||
}
|
||||
}
|
||||
));
|
||||
glassSection.body.appendChild(createToggle(
|
||||
"Show Border",
|
||||
getSettingValue("🔍MagnifyGlass.BorderEnabled", true),
|
||||
(checked) => setSettingValue("🔍MagnifyGlass.BorderEnabled", checked),
|
||||
"Show or hide the glass border"
|
||||
));
|
||||
glassSection.body.appendChild(createToggle(
|
||||
"Follow Cursor",
|
||||
getSettingValue("🔍MagnifyGlass.FollowCursor", false),
|
||||
(checked) => setSettingValue("🔍MagnifyGlass.FollowCursor", checked),
|
||||
`Glass follows the cursor. Hotkey: ${toggleFollowKey}`
|
||||
));
|
||||
glassSection.body.appendChild(createToggle(
|
||||
"Always Active Mode",
|
||||
getSettingValue("🔍MagnifyGlass.AlwaysActiveMode", true),
|
||||
(checked) => setSettingValue("🔍MagnifyGlass.AlwaysActiveMode", checked),
|
||||
`When enabled, glass stays visible. Toggle with ${activationKey}`
|
||||
));
|
||||
container.appendChild(glassSection.section);
|
||||
const hotkeySection = createSection("Hotkeys", true);
|
||||
hotkeySection.body.appendChild(createSelect(
|
||||
"Activation Key",
|
||||
getSettingValue("🔍MagnifyGlass.ActivationKey", "x"),
|
||||
ACTIVATION_KEYS,
|
||||
(value) => setSettingValue("🔍MagnifyGlass.ActivationKey", value),
|
||||
"Key to toggle or hold to activate the magnifying glass"
|
||||
));
|
||||
hotkeySection.body.appendChild(createSelect(
|
||||
"Reset Key",
|
||||
getSettingValue("🔍MagnifyGlass.ResetKey", "o"),
|
||||
RESET_KEYS,
|
||||
(value) => setSettingValue("🔍MagnifyGlass.ResetKey", value),
|
||||
"Key to reset glass position and disable follow cursor"
|
||||
));
|
||||
hotkeySection.body.appendChild(createSelect(
|
||||
"Toggle Follow Key",
|
||||
getSettingValue("🔍MagnifyGlass.ToggleFollowCursorKey", "h"),
|
||||
TOGGLE_FOLLOW_KEYS,
|
||||
(value) => setSettingValue("🔍MagnifyGlass.ToggleFollowCursorKey", value),
|
||||
"Key to toggle follow cursor mode"
|
||||
));
|
||||
hotkeySection.body.appendChild(createToggle(
|
||||
"Require Alt Key",
|
||||
getSettingValue("🔍MagnifyGlass.AltRequired", false),
|
||||
(checked) => setSettingValue("🔍MagnifyGlass.AltRequired", checked),
|
||||
"Require Alt key to be held with activation key"
|
||||
));
|
||||
hotkeySection.body.appendChild(createSlider(
|
||||
"Offset Step",
|
||||
getSettingValue("🔍MagnifyGlass.OffsetStep", 10),
|
||||
1,
|
||||
50,
|
||||
1,
|
||||
"px",
|
||||
(value) => setSettingValue("🔍MagnifyGlass.OffsetStep", value),
|
||||
"Amount to move glass per arrow key press (pixels)"
|
||||
));
|
||||
container.appendChild(hotkeySection.section);
|
||||
const panelSection = createSection("Info Panel", true);
|
||||
const toggleHotkey = getSettingValue("🔍MagnifyGlass.ToggleHotkey", "i").toUpperCase();
|
||||
getSettingValue("🔍MagnifyGlass.GlassPreviewToggleHotkey", "g").toUpperCase();
|
||||
getSettingValue("🔍MagnifyGlass.PinPanelHotkey", "u").toUpperCase();
|
||||
panelSection.body.appendChild(createToggle(
|
||||
"Enable Panel",
|
||||
getSettingValue("🔍MagnifyGlass.InfoPanelEnabled", true),
|
||||
(checked) => setSettingValue("🔍MagnifyGlass.InfoPanelEnabled", checked),
|
||||
`Show info panel with node details. Toggle: ${toggleHotkey}`
|
||||
));
|
||||
panelSection.body.appendChild(createSelect(
|
||||
"Position",
|
||||
getSettingValue("🔍MagnifyGlass.InfoPanelPosition", "Bottom"),
|
||||
PANEL_POSITIONS,
|
||||
(value) => setSettingValue("🔍MagnifyGlass.InfoPanelPosition", value),
|
||||
"Position of the info panel relative to the glass"
|
||||
));
|
||||
panelSection.body.appendChild(createSlider(
|
||||
"Width",
|
||||
getSettingValue("🔍MagnifyGlass.InfoPanelWidth", 300),
|
||||
200,
|
||||
600,
|
||||
20,
|
||||
"px",
|
||||
(value) => setSettingValue("🔍MagnifyGlass.InfoPanelWidth", value),
|
||||
"Width of the info panel",
|
||||
(value) => {
|
||||
var _a, _b;
|
||||
const infoPanel = window.infoPanelManager;
|
||||
if ((_b = (_a = infoPanel == null ? void 0 : infoPanel.stateManager) == null ? void 0 : _a.state) == null ? void 0 : _b.settings) {
|
||||
infoPanel.stateManager.state.settings["🔍MagnifyGlass.InfoPanelWidth"] = value;
|
||||
infoPanel.uiManager.applyStyles();
|
||||
if (infoPanel.magnifyGlass.state.active) {
|
||||
infoPanel.uiManager.show();
|
||||
infoPanel.positionManager.positionPanel();
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
panelSection.body.appendChild(createSlider(
|
||||
"Max Height",
|
||||
getSettingValue("🔍MagnifyGlass.InfoPanelMaxHeight", 300),
|
||||
200,
|
||||
1500,
|
||||
50,
|
||||
"px",
|
||||
(value) => setSettingValue("🔍MagnifyGlass.InfoPanelMaxHeight", value),
|
||||
"Maximum height of the info panel",
|
||||
(value) => {
|
||||
var _a, _b;
|
||||
console.log("[MaxHeight] Handler called with value:", value);
|
||||
const infoPanel = window.infoPanelManager;
|
||||
console.log("[MaxHeight] infoPanelManager:", infoPanel ? "found" : "NOT FOUND");
|
||||
if ((_b = (_a = infoPanel == null ? void 0 : infoPanel.stateManager) == null ? void 0 : _a.state) == null ? void 0 : _b.settings) {
|
||||
console.log("[MaxHeight] Updating setting and calling applyStyles");
|
||||
infoPanel.stateManager.state.settings["🔍MagnifyGlass.InfoPanelMaxHeight"] = value;
|
||||
infoPanel.uiManager.applyStyles();
|
||||
if (infoPanel.magnifyGlass.state.active) {
|
||||
console.log("[MaxHeight] Glass is active, calling show() and positionPanel()");
|
||||
infoPanel.uiManager.show();
|
||||
infoPanel.positionManager.positionPanel();
|
||||
} else {
|
||||
console.log("[MaxHeight] Glass is NOT active, skipping show()");
|
||||
}
|
||||
} else {
|
||||
console.warn("[MaxHeight] Check failed - stateManager/state/settings missing");
|
||||
}
|
||||
}
|
||||
));
|
||||
panelSection.body.appendChild(createSlider(
|
||||
"Opacity",
|
||||
getSettingValue("🔍MagnifyGlass.InfoPanelOpacity", 95),
|
||||
10,
|
||||
100,
|
||||
5,
|
||||
"%",
|
||||
(value) => setSettingValue("🔍MagnifyGlass.InfoPanelOpacity", value),
|
||||
"Background opacity of the info panel",
|
||||
(value) => {
|
||||
var _a, _b;
|
||||
const infoPanel = window.infoPanelManager;
|
||||
if ((_b = (_a = infoPanel == null ? void 0 : infoPanel.stateManager) == null ? void 0 : _a.state) == null ? void 0 : _b.settings) {
|
||||
infoPanel.stateManager.state.settings["🔍MagnifyGlass.InfoPanelOpacity"] = value;
|
||||
infoPanel.uiManager.applyStyles();
|
||||
}
|
||||
}
|
||||
));
|
||||
panelSection.body.appendChild(createColorPicker(
|
||||
"Text Color",
|
||||
getSettingValue("🔍MagnifyGlass.InfoPanelTextColor", "#6b7280"),
|
||||
(value) => setSettingValue("🔍MagnifyGlass.InfoPanelTextColor", value),
|
||||
"Text color in the info panel",
|
||||
(value) => {
|
||||
var _a, _b;
|
||||
const infoPanel = window.infoPanelManager;
|
||||
if ((_b = (_a = infoPanel == null ? void 0 : infoPanel.stateManager) == null ? void 0 : _a.state) == null ? void 0 : _b.settings) {
|
||||
infoPanel.stateManager.state.settings["🔍MagnifyGlass.InfoPanelTextColor"] = value;
|
||||
infoPanel.uiManager.applyStyles();
|
||||
}
|
||||
}
|
||||
));
|
||||
panelSection.body.appendChild(createColorPicker(
|
||||
"Accent Color",
|
||||
getSettingValue("🔍MagnifyGlass.InfoPanelAccentColor", "#3b82f6"),
|
||||
(value) => setSettingValue("🔍MagnifyGlass.InfoPanelAccentColor", value),
|
||||
"Accent color for highlights",
|
||||
(value) => {
|
||||
var _a, _b;
|
||||
const infoPanel = window.infoPanelManager;
|
||||
if ((_b = (_a = infoPanel == null ? void 0 : infoPanel.stateManager) == null ? void 0 : _a.state) == null ? void 0 : _b.settings) {
|
||||
infoPanel.stateManager.state.settings["🔍MagnifyGlass.InfoPanelAccentColor"] = value;
|
||||
infoPanel.uiManager.applyStyles();
|
||||
}
|
||||
}
|
||||
));
|
||||
panelSection.body.appendChild(createToggle(
|
||||
"Animations",
|
||||
getSettingValue("🔍MagnifyGlass.InfoPanelAnimations", false),
|
||||
(checked) => setSettingValue("🔍MagnifyGlass.InfoPanelAnimations", checked),
|
||||
"Enable smooth animations for panel updates"
|
||||
));
|
||||
panelSection.body.appendChild(createToggle(
|
||||
"Hover Controls",
|
||||
getSettingValue("🔍MagnifyGlass.ShowHoveringControls", true),
|
||||
(checked) => setSettingValue("🔍MagnifyGlass.ShowHoveringControls", checked),
|
||||
"Show floating control buttons near the panel"
|
||||
));
|
||||
panelSection.body.appendChild(createSelect(
|
||||
"Controls Position",
|
||||
getSettingValue("🔍MagnifyGlass.ControlsPosition", "bottom"),
|
||||
["top", "bottom", "left", "right", "top-left", "top-right", "bottom-left", "bottom-right"],
|
||||
(value) => setSettingValue("🔍MagnifyGlass.ControlsPosition", value),
|
||||
"Position of floating control buttons"
|
||||
));
|
||||
container.appendChild(panelSection.section);
|
||||
const panelHotkeySection = createSection("Panel Hotkeys", true);
|
||||
panelHotkeySection.body.appendChild(createSelect(
|
||||
"Toggle Key",
|
||||
getSettingValue("🔍MagnifyGlass.ToggleHotkey", "i"),
|
||||
["i", "j", "k", "l", "n", "m"],
|
||||
(value) => setSettingValue("🔍MagnifyGlass.ToggleHotkey", value),
|
||||
"Key to toggle info panel visibility"
|
||||
));
|
||||
panelHotkeySection.body.appendChild(createSelect(
|
||||
"Glass Toggle Key",
|
||||
getSettingValue("🔍MagnifyGlass.GlassPreviewToggleHotkey", "g"),
|
||||
["g", "f", "v", "b"],
|
||||
(value) => setSettingValue("🔍MagnifyGlass.GlassPreviewToggleHotkey", value),
|
||||
"Key to toggle glass preview in panel"
|
||||
));
|
||||
panelHotkeySection.body.appendChild(createSelect(
|
||||
"Pin Key",
|
||||
getSettingValue("🔍MagnifyGlass.PinPanelHotkey", "u"),
|
||||
["u", "p", "y", "t"],
|
||||
(value) => setSettingValue("🔍MagnifyGlass.PinPanelHotkey", value),
|
||||
"Key to pin/unpin the info panel position"
|
||||
));
|
||||
container.appendChild(panelHotkeySection.section);
|
||||
const debugSection = createSection("Debug", true);
|
||||
debugSection.body.appendChild(createToggle(
|
||||
"Debug Mode",
|
||||
getSettingValue("🔍MagnifyGlass.DebugMode", false),
|
||||
(checked) => setSettingValue("🔍MagnifyGlass.DebugMode", checked),
|
||||
"Enable debug logging and visual indicators"
|
||||
));
|
||||
container.appendChild(debugSection.section);
|
||||
const buttonRow = document.createElement("div");
|
||||
buttonRow.style.padding = "16px";
|
||||
buttonRow.style.borderTop = "1px solid var(--border-color, #333)";
|
||||
buttonRow.style.display = "flex";
|
||||
buttonRow.style.flexDirection = "column";
|
||||
buttonRow.style.gap = "8px";
|
||||
const resetBtn = document.createElement("button");
|
||||
resetBtn.className = "magnify-btn";
|
||||
resetBtn.style.width = "100%";
|
||||
resetBtn.innerHTML = `${Icons.reset} Reset Position`;
|
||||
resetBtn.title = `Reset glass position and disable follow cursor. Hotkey: ${resetKey}`;
|
||||
resetBtn.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
const magnifyGlass = window.comfyUIMagnifyGlass;
|
||||
if (magnifyGlass && magnifyGlass.resetOffsets) {
|
||||
magnifyGlass.resetOffsets();
|
||||
const followToggles = container.querySelectorAll(".magnify-toggle-row");
|
||||
followToggles.forEach((row) => {
|
||||
const label = row.querySelector("label");
|
||||
if (label && label.textContent === "Follow Cursor") {
|
||||
const toggle = row.querySelector(".magnify-toggle");
|
||||
if (toggle) toggle.classList.remove("active");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
buttonRow.appendChild(resetBtn);
|
||||
const resetAllBtn = document.createElement("button");
|
||||
resetAllBtn.className = "magnify-btn magnify-btn-secondary";
|
||||
resetAllBtn.style.width = "100%";
|
||||
resetAllBtn.innerHTML = `${Icons.reset} Reset All Settings`;
|
||||
resetAllBtn.title = "Reset all settings to their default values";
|
||||
resetAllBtn.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
if (!confirm("Reset all MagnifyGlass settings to defaults?")) {
|
||||
return;
|
||||
}
|
||||
setSettingValue("🔍MagnifyGlass.ZoomFactor", 300);
|
||||
setSettingValue("🔍MagnifyGlass.GlassSize", 250);
|
||||
setSettingValue("🔍MagnifyGlass.GlassShape", "Rounded Square");
|
||||
setSettingValue("🔍MagnifyGlass.GlassPosition", "Top-Right");
|
||||
setSettingValue("🔍MagnifyGlass.TextureFiltering", "Linear");
|
||||
setSettingValue("🔍MagnifyGlass.BorderWidth", 2);
|
||||
setSettingValue("🔍MagnifyGlass.BorderColor", "#ffffff");
|
||||
setSettingValue("🔍MagnifyGlass.BorderEnabled", true);
|
||||
setSettingValue("🔍MagnifyGlass.FollowCursor", false);
|
||||
setSettingValue("🔍MagnifyGlass.AlwaysActiveMode", true);
|
||||
setSettingValue("🔍MagnifyGlass.ActivationKey", "x");
|
||||
setSettingValue("🔍MagnifyGlass.ResetKey", "o");
|
||||
setSettingValue("🔍MagnifyGlass.ToggleFollowCursorKey", "h");
|
||||
setSettingValue("🔍MagnifyGlass.AltRequired", false);
|
||||
setSettingValue("🔍MagnifyGlass.OffsetStep", 10);
|
||||
setSettingValue("🔍MagnifyGlass.DebugMode", false);
|
||||
setSettingValue("🔍MagnifyGlass.InfoPanelEnabled", true);
|
||||
setSettingValue("🔍MagnifyGlass.InfoPanelPosition", "Bottom");
|
||||
setSettingValue("🔍MagnifyGlass.InfoPanelWidth", 300);
|
||||
setSettingValue("🔍MagnifyGlass.InfoPanelMaxHeight", 600);
|
||||
setSettingValue("🔍MagnifyGlass.InfoPanelOpacity", 95);
|
||||
setSettingValue("🔍MagnifyGlass.InfoPanelTextColor", "#6b7280");
|
||||
setSettingValue("🔍MagnifyGlass.InfoPanelAccentColor", "#3b82f6");
|
||||
setSettingValue("🔍MagnifyGlass.InfoPanelAnimations", false);
|
||||
setSettingValue("🔍MagnifyGlass.ShowHoveringControls", true);
|
||||
setSettingValue("🔍MagnifyGlass.ControlsPosition", "bottom");
|
||||
setSettingValue("🔍MagnifyGlass.ToggleHotkey", "i");
|
||||
setSettingValue("🔍MagnifyGlass.GlassPreviewToggleHotkey", "g");
|
||||
setSettingValue("🔍MagnifyGlass.PinPanelHotkey", "u");
|
||||
const magnifyGlass = window.comfyUIMagnifyGlass;
|
||||
if (magnifyGlass && magnifyGlass.resetOffsets) {
|
||||
magnifyGlass.resetOffsets();
|
||||
}
|
||||
});
|
||||
buttonRow.appendChild(resetAllBtn);
|
||||
container.appendChild(buttonRow);
|
||||
}
|
||||
export {
|
||||
renderSettingsPanel
|
||||
};
|
||||
//# sourceMappingURL=SidebarSettings.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,64 @@
|
||||
import { app } from "/scripts/app.js";
|
||||
import { renderSettingsPanel } from "./SidebarSettings.js";
|
||||
const MAGNIFY_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>`;
|
||||
let sidebarRegistered = false;
|
||||
function loadSidebarStyles() {
|
||||
if (!document.getElementById("magnify-sidebar-styles")) {
|
||||
const link = document.createElement("link");
|
||||
link.id = "magnify-sidebar-styles";
|
||||
link.rel = "stylesheet";
|
||||
link.type = "text/css";
|
||||
link.href = "extensions/comfyui-magnifyglass/sidebar.css";
|
||||
document.head.appendChild(link);
|
||||
}
|
||||
}
|
||||
function renderSidebar(container) {
|
||||
loadSidebarStyles();
|
||||
container.innerHTML = "";
|
||||
const sidebar = document.createElement("div");
|
||||
sidebar.className = "magnify-sidebar";
|
||||
const header = document.createElement("div");
|
||||
header.className = "magnify-sidebar-header";
|
||||
header.innerHTML = `${MAGNIFY_ICON}<h2>Magnify G.</h2>`;
|
||||
sidebar.appendChild(header);
|
||||
const content = document.createElement("div");
|
||||
content.className = "magnify-sidebar-content";
|
||||
renderSettingsPanel(content);
|
||||
sidebar.appendChild(content);
|
||||
container.appendChild(sidebar);
|
||||
}
|
||||
function registerSidebar() {
|
||||
if (sidebarRegistered) {
|
||||
return;
|
||||
}
|
||||
if (!app.extensionManager) {
|
||||
console.warn("MagnifyGlass: extensionManager not available, sidebar registration skipped");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
app.extensionManager.registerSidebarTab({
|
||||
id: "magnifyglass",
|
||||
icon: "pi pi-search",
|
||||
title: "Magnify G.",
|
||||
tooltip: "MagnifyGlass Settings",
|
||||
type: "custom",
|
||||
render: (el) => {
|
||||
renderSidebar(el);
|
||||
}
|
||||
});
|
||||
sidebarRegistered = true;
|
||||
console.log("MagnifyGlass: Sidebar registered successfully");
|
||||
} catch (e) {
|
||||
console.warn("MagnifyGlass: Failed to register sidebar:", e);
|
||||
}
|
||||
}
|
||||
function initSidebar() {
|
||||
setTimeout(() => {
|
||||
registerSidebar();
|
||||
}, 100);
|
||||
}
|
||||
export {
|
||||
initSidebar,
|
||||
registerSidebar
|
||||
};
|
||||
//# sourceMappingURL=sidebar.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"sidebar.js","sources":["../../src/sidebar/sidebar.ts"],"sourcesContent":["/**\n * ComfyUI MagnifyGlass - Sidebar Entry Point\n * \n * Registers the MagnifyGlass sidebar tab with ComfyUI.\n */\n\n// @ts-ignore\nimport { app } from \"/scripts/app.js\";\nimport { renderSettingsPanel } from './SidebarSettings';\n\n/**\n * Magnify glass icon SVG\n */\nconst MAGNIFY_ICON = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"11\" cy=\"11\" r=\"8\"></circle><line x1=\"21\" y1=\"21\" x2=\"16.65\" y2=\"16.65\"></line></svg>`;\n\n/** Track if sidebar has been registered */\nlet sidebarRegistered = false;\n\n/**\n * Load sidebar CSS\n */\nfunction loadSidebarStyles(): void {\n if (!document.getElementById('magnify-sidebar-styles')) {\n const link = document.createElement('link');\n link.id = 'magnify-sidebar-styles';\n link.rel = 'stylesheet';\n link.type = 'text/css';\n link.href = 'extensions/comfyui-magnifyglass/sidebar.css';\n document.head.appendChild(link);\n }\n}\n\n/**\n * Render the sidebar content\n */\nfunction renderSidebar(container: HTMLElement): void {\n // Load styles\n loadSidebarStyles();\n\n // Clear existing content to prevent duplicates\n container.innerHTML = '';\n\n // Create main container\n const sidebar = document.createElement('div');\n sidebar.className = 'magnify-sidebar';\n\n // Header\n const header = document.createElement('div');\n header.className = 'magnify-sidebar-header';\n header.innerHTML = `${MAGNIFY_ICON}<h2>Magnify G.</h2>`;\n sidebar.appendChild(header);\n\n // Content area\n const content = document.createElement('div');\n content.className = 'magnify-sidebar-content';\n\n // Render settings panel (contains all settings)\n renderSettingsPanel(content);\n\n sidebar.appendChild(content);\n container.appendChild(sidebar);\n}\n\n/**\n * Register the sidebar tab with ComfyUI\n */\nexport function registerSidebar(): void {\n // Prevent duplicate registration\n if (sidebarRegistered) {\n return;\n }\n\n // Wait for app to be ready\n if (!app.extensionManager) {\n console.warn('MagnifyGlass: extensionManager not available, sidebar registration skipped');\n return;\n }\n\n try {\n app.extensionManager.registerSidebarTab({\n id: 'magnifyglass',\n icon: 'pi pi-search',\n title: 'Magnify G.',\n tooltip: 'MagnifyGlass Settings',\n type: 'custom',\n render: (el: HTMLElement) => {\n renderSidebar(el);\n }\n });\n\n sidebarRegistered = true;\n console.log('MagnifyGlass: Sidebar registered successfully');\n } catch (e) {\n console.warn('MagnifyGlass: Failed to register sidebar:', e);\n }\n}\n\n/**\n * Initialize sidebar (called after app setup)\n */\nexport function initSidebar(): void {\n // Register on next tick to ensure app is ready\n setTimeout(() => {\n registerSidebar();\n }, 100);\n}\n"],"names":[],"mappings":";;AAaA,MAAM,eAAe;AAGrB,IAAI,oBAAoB;AAKxB,SAAS,oBAA0B;AAC/B,MAAI,CAAC,SAAS,eAAe,wBAAwB,GAAG;AACpD,UAAM,OAAO,SAAS,cAAc,MAAM;AAC1C,SAAK,KAAK;AACV,SAAK,MAAM;AACX,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,aAAS,KAAK,YAAY,IAAI;AAAA,EAClC;AACJ;AAKA,SAAS,cAAc,WAA8B;AAEjD,oBAAA;AAGA,YAAU,YAAY;AAGtB,QAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,UAAQ,YAAY;AAGpB,QAAM,SAAS,SAAS,cAAc,KAAK;AAC3C,SAAO,YAAY;AACnB,SAAO,YAAY,GAAG,YAAY;AAClC,UAAQ,YAAY,MAAM;AAG1B,QAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,UAAQ,YAAY;AAGpB,sBAAoB,OAAO;AAE3B,UAAQ,YAAY,OAAO;AAC3B,YAAU,YAAY,OAAO;AACjC;AAKO,SAAS,kBAAwB;AAEpC,MAAI,mBAAmB;AACnB;AAAA,EACJ;AAGA,MAAI,CAAC,IAAI,kBAAkB;AACvB,YAAQ,KAAK,4EAA4E;AACzF;AAAA,EACJ;AAEA,MAAI;AACA,QAAI,iBAAiB,mBAAmB;AAAA,MACpC,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,OAAO;AAAA,MACP,SAAS;AAAA,MACT,MAAM;AAAA,MACN,QAAQ,CAAC,OAAoB;AACzB,sBAAc,EAAE;AAAA,MACpB;AAAA,IAAA,CACH;AAED,wBAAoB;AACpB,YAAQ,IAAI,+CAA+C;AAAA,EAC/D,SAAS,GAAG;AACR,YAAQ,KAAK,6CAA6C,CAAC;AAAA,EAC/D;AACJ;AAKO,SAAS,cAAoB;AAEhC,aAAW,MAAM;AACb,oBAAA;AAAA,EACJ,GAAG,GAAG;AACV;"}
|
||||
Reference in New Issue
Block a user