feat: Introduce a new sidebar for settings and inspector, and add glass reset options and info panel positioning improvements.

This commit is contained in:
AEmotionStudio
2026-01-01 19:47:32 -08:00
parent 452b1776b9
commit 514e760d5f
43 changed files with 4138 additions and 67 deletions
+664
View File
@@ -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
View File
@@ -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);
}
+2
View File
@@ -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();
}
+7
View File
@@ -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;
+2 -2
View File
@@ -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
View File
@@ -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)}%` },
+2 -2
View File
@@ -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);
+5 -2
View File
@@ -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;
}
+4
View File
@@ -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');
}
});
+3 -2
View File
@@ -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
+78
View File
@@ -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);
}
}
});
}
+261
View File
@@ -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;
}
+678
View File
@@ -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);
}
+8
View File
@@ -0,0 +1,8 @@
/**
* ComfyUI MagnifyGlass - Sidebar Index
*
* Re-exports all sidebar modules.
*/
export { registerSidebar, initSidebar } from './sidebar';
export { renderSettingsPanel } from './SidebarSettings';
+106
View File
@@ -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);
}
+1
View File
@@ -215,6 +215,7 @@ export interface MagnifyGlassInstance {
updateMagnifiedView(): void;
applyUiChanges(): void;
toggle(): void;
resetOffsets(): void;
}
export interface MagnifyGlassState {
+664
View File
@@ -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
View File
@@ -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;
}
+1
View File
@@ -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
+5
View File
@@ -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
+3 -3
View File
@@ -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
View File
@@ -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
+2 -2
View File
@@ -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
+3 -2
View File
@@ -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
+2
View File
@@ -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
View File
@@ -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;"}
+4 -2
View File
@@ -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
View File
@@ -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;"}
+71
View File
@@ -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
View File
@@ -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);
}
+164
View File
@@ -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
+552
View File
@@ -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
+64
View File
@@ -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
+1
View File
@@ -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;"}