| // Copyright (C) 2025 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| .pf-canvas { |
| height: 100%; |
| overflow: hidden; |
| background: linear-gradient( |
| 0deg, |
| var(--pf-color-border-secondary) 1px, |
| transparent 1px |
| ), |
| linear-gradient( |
| 90deg, |
| var(--pf-color-border-secondary) 1px, |
| transparent 1px |
| ); |
| background-size: 20px 20px; |
| background-color: var(--pf-color-background); |
| position: relative; |
| user-select: none; |
| font-family: var(--pf-font-compact); |
| cursor: grab; |
| } |
| |
| .pf-canvas-content { |
| position: absolute; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| pointer-events: none; |
| overflow: visible; |
| } |
| |
| .pf-nodegraph-controls { |
| position: absolute; |
| top: 16px; |
| right: 16px; |
| z-index: 100; |
| pointer-events: auto; |
| display: flex; |
| gap: 8px; |
| } |
| |
| .pf-node { |
| position: absolute; |
| background: var(--pf-color-background-secondary); |
| border: 2px solid var(--pf-color-border); |
| border-radius: 8px; |
| min-width: 180px; |
| box-shadow: 0 4px 12px var(--pf-color-box-shadow); |
| cursor: move; |
| pointer-events: auto; |
| padding-block: 12px; |
| } |
| |
| // Wrapper for dock chains - uses flexbox to make children same width |
| .pf-dock-chain { |
| position: absolute; |
| display: flex; |
| flex-direction: column; |
| pointer-events: auto; |
| box-shadow: 0 4px 12px var(--pf-color-box-shadow); |
| |
| // All nodes in chain stretch to same width (determined by widest node) |
| .pf-node { |
| width: 100%; |
| position: relative !important; |
| left: auto !important; |
| top: auto !important; |
| box-shadow: none; // No box shadow for docked nodes |
| } |
| } |
| |
| .pf-node.pf-selected { |
| border-color: var(--pf-color-accent); |
| box-shadow: 0 0 0 2px |
| color-mix(in srgb, var(--pf-color-accent) 30%, transparent); |
| z-index: 1; // Make sure box shadow appears above other nodes |
| } |
| |
| // Docked child: flush top edge with no rounded corners or border |
| .pf-node.pf-docked-child { |
| border-top-left-radius: 0; |
| border-top-right-radius: 0; |
| margin-top: -2px; // Overlap border with parent |
| } |
| |
| // Parent with docked child: flush bottom edge with no rounded corners |
| .pf-node.pf-has-docked-child { |
| border-bottom-left-radius: 0; |
| border-bottom-right-radius: 0; |
| } |
| |
| // Dock target highlight with pulsing animation |
| .pf-node.pf-dock-target { |
| box-shadow: 0 4px 0 3px var(--pf-color-accent); |
| animation: pulse-dock 0.6s ease-in-out infinite; |
| } |
| |
| @keyframes pulse-dock { |
| 0%, |
| 100% { |
| box-shadow: 0 4px 0 3px var(--pf-color-accent); |
| } |
| 50% { |
| box-shadow: 0 6px 0 5px var(--pf-color-accent); |
| opacity: 0.95; |
| } |
| } |
| |
| .pf-node-header { |
| background: var(--pf-color-background); |
| padding: 8px 12px; |
| border-radius: 6px 6px 0 0; |
| color: var(--pf-color-text); |
| font-weight: bold; |
| font-size: 14px; |
| border-bottom: 1px solid var(--pf-color-border); |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| gap: 8px; |
| } |
| |
| .pf-node-title { |
| flex: 1; |
| } |
| |
| .pf-node-content { |
| padding-inline: 12px; |
| } |
| |
| .pf-port-row { |
| position: relative; |
| margin: 8px 0; |
| color: var(--pf-color-text-muted); |
| font-size: 13px; |
| padding: 4px 16px; |
| |
| .pf-port { |
| transform: translateY(-50%); |
| } |
| } |
| |
| .pf-port-input { |
| text-align: left; |
| } |
| |
| .pf-port-output { |
| text-align: right; |
| } |
| |
| .pf-port { |
| width: 16px; |
| height: 16px; |
| border-radius: 50%; |
| background: var(--pf-color-border); |
| border: 2px solid var(--pf-color-border-secondary); |
| cursor: crosshair; |
| position: absolute; |
| top: 50%; |
| z-index: 10; |
| } |
| |
| .pf-output:hover { |
| background: var(--pf-color-accent); |
| border-color: var(--pf-color-accent); |
| } |
| |
| .pf-port.pf-connected { |
| background: var(--pf-color-accent); |
| border-color: var(--pf-color-accent); |
| } |
| |
| .pf-port.pf-input { |
| left: -8px; |
| } |
| |
| .pf-port.pf-port-top { |
| top: 0; |
| left: 50%; |
| transform: translateY(-50%); |
| } |
| |
| .pf-port.pf-port-bottom { |
| top: unset; |
| bottom: 0; |
| left: 50%; |
| transform: translateY(50%); |
| } |
| |
| .pf-port.pf-output { |
| right: -8px; |
| } |
| |
| svg { |
| position: absolute; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| pointer-events: none; |
| z-index: 1; |
| overflow: visible; |
| } |
| |
| .pf-connection { |
| stroke: var(--pf-color-accent); |
| stroke-width: 2; |
| fill: none; |
| filter: drop-shadow( |
| 0 0 4px color-mix(in srgb, var(--pf-color-accent) 50%, transparent) |
| ); |
| transition: |
| stroke-width 0.2s, |
| stroke 0.2s; |
| } |
| |
| .pf-connection:hover { |
| stroke: var(--pf-color-danger); |
| stroke-width: 3; |
| filter: drop-shadow( |
| 0 0 6px color-mix(in srgb, var(--pf-color-danger) 80%, transparent) |
| ); |
| } |
| |
| .pf-temp-connection { |
| stroke: var(--pf-color-text-muted); |
| stroke-width: 2; |
| fill: none; |
| stroke-dasharray: 5, 5; |
| } |
| |
| .pf-connecting { |
| cursor: crosshair; |
| |
| .pf-input:hover { |
| background: var(--pf-color-accent); |
| border-color: var(--pf-color-accent); |
| cursor: copy; |
| } |
| .pf-output:hover { |
| background: var(--pf-color-border); |
| border-color: var(--pf-color-border-secondary); |
| cursor: not-allowed; |
| } |
| } |
| |
| .pf-output.pf-active, |
| .pf-output.pf-active:hover { |
| background: var(--pf-color-accent); |
| border-color: var(--pf-color-accent); |
| cursor: crosshair; |
| } |
| |
| .pf-panning { |
| cursor: grabbing; |
| } |