:root {
--color-bg-primary: #f8f9fa;
--color-bg-secondary: #ffffff;
--color-text-primary: #212529;
--color-text-secondary: #6c757d;
--color-accent: #3498db;
--color-accent-hover: #2980b9;
--color-border: #dee2e6;
--color-thread: #3498db;
--color-resource: #2ecc71;
--color-attempt: #f39c12;
--color-acquired: #27ae60;
--color-deadlock: #e74c3c;
--color-button: #f0f0f0;
--color-button-text: #333;
--color-button-hover: #e0e0e0;
--color-shadow: rgba(0, 0, 0, 0.1);
--color-modal-bg: rgba(0, 0, 0, 0.5);
--bg-color: #f4f2f2ff;
--bg-gradient-end: #e0e9f5;
--container-bg: #ffffff;
--text-color: #333333;
--primary-color: #ce412cff;
--primary-dark: #a53423ff;
--success-color: #27ae60;
--warning-color: #f39c12;
--danger-color: #e74c3c;
--danger-dark: #c0392b;
--neutral-color: #7f8c8d;
--light-gray: #ecf0f1;
--border-color: #e0e0e0;
--hover-bg: #f8f9fa;
--shadow-color: rgba(0, 0, 0, 0.1);
--tooltip-bg: rgba(0, 0, 0, 0.8);
--mutex-fill: #2ecc71;
--mutex-stroke: #27ae60;
--rwlock-fill: #f39c12;
--rwlock-stroke: #e67e22;
--condvar-fill: #9b59b6;
--condvar-stroke: #8e44ad;
--thread-fill: #e74c3c;
--thread-stroke: #c0392b;
--thread-main-fill: #3498db;
--thread-main-stroke: #2980b9;
--link-deadlock: #ff1f1f;
--link-attempt: #f1c40f;
--link-wait: #f06292;
--link-acquired: #27ae60;
--link-acquired-read: #a8f5c2;
--link-acquired-write: #0e5a34;
--link-released: #95a5a6;
--link-notify: #00bcd4;
--link-spawn: #d76250;
}
.upload-item-name {
font-weight: 500;
flex-grow: 1;
}
.upload-item-size {
color: var(--text-secondary);
margin: 0 12px;
}
.upload-item-view, .upload-item-load {
background-color: var(--primary-color);
color: white;
border: none;
padding: 4px 8px;
border-radius: 4px;
cursor: pointer;
font-size: 0.8rem;
margin-left: 4px;
}
.upload-item-view:hover, .upload-item-load:hover {
background-color: var(--primary-dark);
}
.upload-item-load {
background-color: var(--success-color);
}
.upload-item-load:hover {
background-color: var(--success-color);
opacity: 0.9;
}
.welcome-message {
text-align: center;
padding: 25px;
}
.welcome-message i {
font-size: 2.5rem;
color: var(--primary-color);
margin-bottom: 15px;
}
.welcome-logo {
width: 70px;
height: auto;
margin-bottom: 15px;
}
.welcome-message h2 {
margin-bottom: 12px;
color: var(--primary-color);
}
.welcome-message p {
font-size: 1rem;
color: var(--text-secondary);
}
#json-preview {
margin-top: 15px;
display: none;
}
#json-preview h3 {
margin-bottom: 8px;
color: var(--primary-color);
}
#json-content {
background-color: var(--hover-bg);
padding: 12px;
border-radius: 6px;
overflow-x: auto;
max-height: 250px;
font-family: 'Roboto Mono', monospace;
font-size: 0.85rem;
white-space: pre-wrap;
color: var(--text-color);
border: 1px solid var(--border-color);
}
.scenario-info {
background-color: var(--container-bg);
border-radius: 8px;
box-shadow: 0 4px 12px var(--shadow-color);
padding: 15px;
margin-bottom: 15px;
border-left: 4px solid var(--primary-color);
}
.scenario-info h2 {
color: var(--primary-color);
margin-bottom: 8px;
font-size: 1.4rem;
}
.scenario-description {
line-height: 1.4;
margin-bottom: 0;
}
.share-link-container {
display: flex;
margin: 15px 0;
gap: 8px;
}
#share-link {
flex-grow: 1;
padding: 8px 12px;
border-radius: 6px;
border: 1px solid var(--border-color);
background-color: var(--hover-bg);
color: var(--text-color);
font-family: 'Roboto Mono', monospace;
font-size: 0.9rem;
}
#copy-link-btn {
background-color: var(--primary-color);
min-width: 90px;
}
.copy-success {
display: flex;
align-items: center;
gap: 8px;
padding: 8px;
background-color: rgba(46, 204, 113, 0.1);
border-radius: 6px;
color: var(--success-color);
margin-top: 8px;
}
.copy-success i {
font-size: 1.1rem;
}
.main-thread {
font-family: 'Roboto Mono', monospace;
color: var(--thread-main-fill);
font-weight: 700;
position: relative;
font-size: 0.95em;
background-color: rgba(52, 152, 219, 0.12);
padding: 3px 6px;
border-radius: 4px;
border: 1px solid rgba(41, 128, 185, 0.25);
}
.thread-id {
font-family: 'Roboto Mono', monospace;
color: var(--thread-fill);
font-weight: 700;
position: relative;
font-size: 0.9em;
background-color: rgba(231, 76, 60, 0.12);
padding: 2px 5px;
border-radius: 4px;
border: 1px solid rgba(192, 57, 43, 0.25);
}
.resource-id {
font-family: 'Roboto Mono', monospace;
color: var(--primary-color);
font-weight: 600;
position: relative;
font-size: 0.9em;
}
.resource-id.mutex {
color: var(--mutex-fill);
background-color: color-mix(in srgb, var(--mutex-fill) 15%, transparent);
padding: 2px 5px;
border-radius: 3px;
border: 1px solid color-mix(in srgb, var(--mutex-stroke) 20%, transparent);
}
.resource-id.rwlock {
color: var(--rwlock-fill);
background-color: color-mix(in srgb, var(--rwlock-fill) 15%, transparent);
padding: 2px 5px;
border-radius: 3px;
border: 1px solid color-mix(in srgb, var(--rwlock-stroke) 20%, transparent);
}
.resource-id.condvar {
color: var(--condvar-fill);
background-color: color-mix(in srgb, var(--condvar-fill) 15%, transparent);
padding: 2px 5px;
border-radius: 3px;
border: 1px solid color-mix(in srgb, var(--condvar-stroke) 20%, transparent);
}
#controls, #graph, #step-info, #wait-graph, #timeline, #legend, .scenario-info, #loading {
background-color: var(--container-bg);
border-radius: 12px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08);
overflow: hidden;
backdrop-filter: blur(8px);
border: 1px solid rgba(255, 255, 255, 0.1);
transition: box-shadow 0.2s ease;
position: relative;
z-index: 1;
}
#controls:hover, #graph:hover, #step-info:hover, #wait-graph:hover, #timeline:hover, #legend:hover {
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.12);
}
#step-info, #wait-graph {
position: relative;
border-left: none;
}
#step-info::before, #wait-graph::before {
content: '';
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 4px;
background: linear-gradient(to bottom, var(--primary-color), var(--primary-dark));
border-radius: 4px 0 0 4px;
}
#wait-graph::before {
background: linear-gradient(to bottom, var(--warning-color), var(--danger-color));
}
[data-theme="dark"] {
--color-bg-primary: #121212;
--color-bg-secondary: #1e1e1e;
--color-text-primary: #f8f9fa;
--color-text-secondary: #adb5bd;
--color-accent: #61dafb;
--color-accent-hover: #4dc0e6;
--color-border: #343a40;
--color-thread: #61dafb;
--color-resource: #5bd75b;
--color-attempt: #ffc107;
--color-acquired: #5bd75b;
--color-deadlock: #ff5252;
--color-button: #333;
--color-button-text: #f0f0f0;
--color-button-hover: #444;
--color-shadow: rgba(0, 0, 0, 0.3);
--bg-color: #121212;
--bg-gradient-end: #1a2333;
--container-bg: #1e1e1e;
--text-color: #f5f5f5;
--primary-color: #ce412cff;
--primary-dark: #a53423ff;
--success-color: #2ecc71;
--warning-color: #f1c40f;
--danger-color: #e74c3c;
--danger-dark: #c0392b;
--neutral-color: #95a5a6;
--light-gray: #2c3e50;
--border-color: #34495e;
--hover-bg: #2c3e50;
--shadow-color: rgba(0, 0, 0, 0.3);
--tooltip-bg: rgba(0, 0, 0, 0.9);
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
ul {
list-style-type: none;
}
body {
font-family: 'Inter', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, var(--bg-color) 0%, var(--bg-gradient-end) 100%);
background-attachment: fixed;
color: var(--text-color);
margin: 0;
padding: 0;
transition: background-color 0.1s, color 0.1s;
min-height: 100vh;
display: flex;
flex-direction: column;
position: relative;
}
body::before {
content: none;
}
.container {
max-width: 1400px;
margin: 0 auto;
padding: 15px;
width: 100%;
flex: 1;
}
header {
background-color: var(--primary-color);
color: white;
padding: 12px 15px;
box-shadow: 0 2px 10px var(--shadow-color);
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 5px;
border-radius: 8px;
}
.logo {
display: flex;
align-items: center;
gap: 8px;
}
.logo i {
font-size: 1.8rem;
}
.logo h1 {
margin: 0;
font-size: 1.6rem;
font-weight: 700;
color: white;
}
.header-buttons {
display: flex;
gap: 8px;
align-items: center;
}
#theme-toggle, .header-btn {
background-color: rgba(255, 255, 255, 0.15);
border: none;
color: white;
display: flex;
align-items: center;
justify-content: center;
padding: 6px 10px;
border-radius: 6px;
cursor: pointer;
font-size: 0.9rem;
transition: background-color 0.1s;
}
#theme-toggle:hover, .header-btn:hover {
background-color: rgba(255, 255, 255, 0.25);
}
.cycle-visualization .thread-id {
padding: 3px 6px;
background-color: rgba(244, 67, 54, 0.1);
border-radius: 4px;
border: 1px solid rgba(244, 67, 54, 0.2);
font-size: 0.9em;
display: inline-block;
}
.cycle-visualization i {
color: #f44336;
opacity: 0.8;
font-size: 0.9em;
margin: 0 3px;
}
.deadlock-explanation {
font-size: 0.9em;
color: #757575;
margin-top: 8px;
padding: 8px;
background-color: rgba(244, 67, 54, 0.05);
border-radius: 4px;
border-left: 3px solid #f44336;
}
#theme-toggle i, .header-btn i {
margin-right: 6px;
font-size: 1.1rem;
}
main {
display: flex;
flex-direction: column;
gap: 0px;
}
#loading {
text-align: center;
padding: 30px;
background-color: var(--container-bg);
border-radius: 8px;
box-shadow: 0 4px 12px var(--shadow-color);
}
.spinner {
border: 4px solid rgba(0, 0, 0, 0.1);
border-radius: 50%;
border-top: 4px solid var(--primary-color);
width: 40px;
height: 40px;
margin: 0 auto 15px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.visualization-container {
display: flex;
gap: 8px;
margin: 5px 0;
min-height: 540px;
}
.visualization-left {
flex: 3;
min-width: 0;
}
.visualization-right {
flex: 2;
display: flex;
flex-direction: column;
gap: 8px;
height: 540px;
overflow-y: auto;
padding-right: 8px;
padding-bottom: 5px;
}
.visualization-right > div {
flex-shrink: 0;
width: 100%;
}
.visualization-right::-webkit-scrollbar {
width: 6px;
}
.visualization-right::-webkit-scrollbar-track {
background: var(--hover-bg);
border-radius: 10px;
}
.visualization-right::-webkit-scrollbar-thumb {
background: var(--primary-color);
border-radius: 10px;
}
.visualization-right::-webkit-scrollbar-thumb:hover {
background: var(--primary-dark);
}
#graph {
background-color: var(--container-bg);
border-radius: 8px;
box-shadow: 0 4px 12px var(--shadow-color);
padding: 15px;
height: 540px;
position: relative;
overflow: hidden;
display: flex !important;
justify-content: center;
align-items: center;
}
.tooltip {
position: absolute;
background-color: rgba(0, 0, 0, 0.75);
color: white;
padding: 8px 12px;
border-radius: 6px;
font-size: 14px;
pointer-events: none;
opacity: 0;
transition: opacity 0.2s ease;
z-index: 1000;
max-width: 300px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
white-space: nowrap;
line-height: 1.4;
}
.node {
cursor: pointer;
}
.node:hover circle {
stroke-width: 3px;
filter: brightness(1.2);
transform: scale(1.1);
fill-opacity: 1;
}
.node circle {
transition: r 0.3s ease, fill 0.3s ease, stroke 0.3s ease, stroke-width 0.3s ease,
stroke-dasharray 0.3s ease, filter 0.4s ease, fill-opacity 0.3s ease, transform 0.3s ease;
fill-opacity: 0.9;
}
.node text {
font-size: 13px;
font-weight: bold;
fill: white;
pointer-events: none;
user-select: none;
text-anchor: middle;
dominant-baseline: middle;
stroke: none !important;
paint-order: normal;
}
.node:hover text {
fill: #ffffff;
text-shadow: none;
stroke: none;
}
.resource circle {
fill: var(--primary-color);
stroke: var(--primary-dark);
stroke-width: 2px;
}
.thread circle {
fill: var(--danger-color);
stroke: var(--danger-dark);
stroke-width: 2px;
}
.node.thread[data-in-cycle="true"] circle {
fill: var(--danger-color);
stroke: #ff0000;
stroke-width: 2px;
stroke-dasharray: 3;
animation: pulse 1.5s infinite alternate;
}
@keyframes pulse {
from {
box-shadow: 0 0 0 0 rgba(255, 0, 0, 0.6);
stroke-width: 2px;
}
to {
box-shadow: 0 0 0 4px rgba(255, 0, 0, 0);
stroke-width: 3px;
}
}
.link {
stroke: var(--neutral-color);
stroke-width: 2px;
transition: stroke 0.3s ease, stroke-width 0.3s ease, opacity 0.3s ease, filter 0.4s ease;
stroke-linecap: round;
stroke-linejoin: round;
}
.link.attempt {
stroke: var(--warning-color);
stroke-dasharray: 5,3;
stroke-width: 2.5px;
animation: dash 20s linear infinite;
}
@keyframes dash {
to {
stroke-dashoffset: -1000;
}
}
.link.acquired {
stroke: var(--success-color);
stroke-width: 3px;
filter: drop-shadow(0 0 2px rgba(39, 174, 96, 0.5));
}
.link.released {
stroke: var(--neutral-color);
stroke-width: 2.5px;
stroke-dasharray: 5;
}
.link.spawn {
stroke: var(--link-spawn);
stroke-width: 2.5px;
}
.link.exit {
stroke: #34495e;
stroke-width: 2.5px;
stroke-dasharray: 2;
}
.link.deadlock {
stroke: var(--link-deadlock);
stroke-width: 3px;
animation: pulse-link 3s infinite alternate;
stroke-opacity: 0.9;
filter: drop-shadow(0 0 3px rgba(244, 67, 54, 0.7));
z-index: 1000;
}
.woken-outline {
stroke: var(--link-notify) !important;
fill: none !important;
stroke-dasharray: 4,3;
pointer-events: none;
}
@keyframes pulse-link {
from {
stroke-opacity: 0.8;
filter: drop-shadow(0 0 2px rgba(244, 67, 54, 0.6));
}
to {
stroke-opacity: 1;
filter: drop-shadow(0 0 3px rgba(244, 67, 54, 0.8));
}
}
#controls {
display: flex;
justify-content: center;
align-items: center;
gap: 8px;
margin: 5px 0;
padding: 8px;
background-color: var(--container-bg);
border-radius: 8px;
box-shadow: 0 2px 8px var(--shadow-color);
}
.control-btn {
background-color: var(--primary-color);
color: white;
border: none;
padding: 8px 15px;
border-radius: 6px;
cursor: pointer;
font-size: 0.95rem;
font-weight: 500;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 4px var(--shadow-color);
transition: all 0.1s ease;
min-width: 110px;
flex: 1;
max-width: 160px;
}
.control-btn i {
margin-right: 6px;
}
.control-btn:hover {
background-color: var(--primary-dark);
transform: translateY(-2px);
box-shadow: 0 4px 8px var(--shadow-color);
}
.control-btn:active {
transform: translateY(0);
}
.control-btn:disabled {
background-color: var(--neutral-color);
cursor: not-allowed;
transform: none;
opacity: 0.7;
}
#step-info {
background-color: var(--container-bg);
border-radius: 8px;
box-shadow: 0 4px 12px var(--shadow-color);
padding: 12px;
border-left: 5px solid var(--primary-color);
overflow-wrap: break-word;
word-break: break-word;
min-height: fit-content;
width: 100%;
margin-bottom: 0;
}
#step-info h3 {
color: var(--primary-color);
margin-bottom: 10px;
font-size: 1.1rem;
}
#step-info p {
margin-bottom: 8px;
line-height: 1.4;
}
.code-reference {
font-family: 'Roboto Mono', monospace;
background-color: var(--hover-bg);
padding: 4px 6px;
border-radius: 4px;
font-size: 0.9em;
border: 1px solid var(--border-color);
}
#wait-graph {
background-color: var(--container-bg);
border-radius: 8px;
box-shadow: 0 4px 12px var(--shadow-color);
padding: 12px;
border-left: 5px solid var(--warning-color);
overflow-wrap: break-word;
word-break: break-word;
min-height: fit-content;
width: 100%;
margin-bottom: 0;
}
#wait-graph h3 {
color: var(--warning-color);
margin-bottom: 10px;
font-size: 1.1rem;
}
#wait-graph-content {
line-height: 1.4;
}
#wait-graph-content ul {
margin-top: 8px;
margin-left: 15px;
}
#wait-graph-content li {
margin-bottom: 4px;
}
#timeline {
background-color: var(--container-bg);
border-radius: 8px;
box-shadow: 0 4px 12px var(--shadow-color);
padding: 12px;
height: 70px;
position: relative;
margin: 5px 0;
}
#timeline-line {
position: absolute;
width: calc(100% - 30px);
height: 3px;
background-color: var(--border-color);
top: 50%;
left: 15px;
}
#timeline-marker {
position: absolute;
height: 50px;
width: 3px;
background-color: var(--primary-color);
top: 50%;
transform: translateY(-50%);
transition: left 0.1s ease;
z-index: 5;
}
.timeline-event {
position: absolute;
width: 14px;
height: 14px;
border-radius: 50%;
top: 50%;
transform: translate(-50%, -50%);
cursor: pointer;
z-index: 10;
box-shadow: 0 2px 5px var(--shadow-color);
transition: transform 0.1s ease, box-shadow 0.1s ease;
}
.timeline-event:hover {
transform: translate(-50%, -50%) scale(1.3);
box-shadow: 0 0 8px var(--primary-color);
}
.timeline-event.init {
background-color: whitesmoke;
}
.timeline-event.attempt {
background-color: var(--link-attempt);
}
.timeline-event.acquired {
background-color: var(--link-acquired);
}
.timeline-event.released {
background-color: var(--link-released);
}
.timeline-event.wait {
background-color: var(--link-wait);
}
.timeline-event.notify {
background-color: var(--link-notify);
}
.timeline-event.spawn {
background-color: var(--link-spawn);
}
.timeline-event.exit {
background-color: #34495e;
}
.timeline-event.deadlock {
background-color: var(--link-deadlock);
width: 18px;
height: 18px;
}
#legend {
background-color: var(--container-bg);
border-radius: 12px;
padding: 8px;
box-shadow: 0 3px 15px rgba(0, 0, 0, 0.15);
position: relative;
margin: 5px 0;
z-index: 10;
border: 1px solid rgba(255, 255, 255, 0.1);
backdrop-filter: blur(5px);
display: block;
transition: opacity 0.1s ease;
}
.legend-sections {
display: flex;
gap: 10px;
flex-wrap: wrap;
align-items: stretch;
}
.legend-section {
flex: 1 1 280px;
background: rgba(0,0,0,0.03);
border-radius: 8px;
padding: 8px;
}
.legend-title {
font-size: 0.8rem;
font-weight: 700;
color: var(--primary-color);
margin-bottom: 6px;
text-transform: uppercase;
}
#legend h3 {
margin: 0 0 8px 0;
font-size: 0.85rem;
color: var(--primary-color);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.legend-items {
display: flex;
flex-wrap: wrap;
gap: 6px;
justify-content: flex-start;
}
.legend-item {
display: flex;
align-items: center;
gap: 5px;
font-size: 0.78rem;
background: rgba(0, 0, 0, 0.02);
padding: 2px 6px;
border-radius: 5px;
transition: all 0.2s ease;
flex: 0 1 auto;
min-width: 0;
justify-content: flex-start;
}
.legend-item:hover {
background: rgba(0, 0, 0, 0.06);
transform: translateY(-2px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}
.legend-color {
width: 14px;
height: 14px;
border-radius: 50%;
}
.legend-color.thread {
background-color: var(--danger-color);
border: 2px solid var(--danger-dark);
}
.legend-color.mutex {
background-color: var(--mutex-fill);
border: 2px solid var(--mutex-stroke);
}
.legend-color.rwlock {
background-color: var(--rwlock-fill);
border: 2px solid var(--rwlock-stroke);
}
.legend-color.condvar {
background-color: var(--condvar-fill);
border: 2px solid var(--condvar-stroke);
}
.node.mutex {
fill: var(--mutex-fill) !important;
stroke: var(--mutex-stroke) !important;
}
.node.rwlock {
fill: var(--rwlock-fill) !important;
stroke: var(--rwlock-stroke) !important;
}
.node.condvar {
fill: var(--condvar-fill) !important;
stroke: var(--condvar-stroke) !important;
}
.node.thread {
fill: var(--thread-fill) !important;
stroke: var(--thread-stroke) !important;
}
.legend-line {
width: 30px;
height: 4px;
border-radius: 2px;
}
.legend-line.attempt {
background-color: var(--warning-color);
background-image: linear-gradient(to right, var(--warning-color) 50%, transparent 50%);
background-size: 8px 100%;
background-repeat: repeat-x;
}
.legend-line.wait {
background-color: var(--link-wait);
background-image: linear-gradient(to right, var(--link-wait) 50%, transparent 50%);
background-size: 6px 100%;
background-repeat: repeat-x;
}
.legend-line.acquired {
background-color: var(--success-color);
}
.legend-line.rw-read-acquired {
background-color: var(--link-acquired-read);
background-image: linear-gradient(to right, var(--link-acquired-read) 50%, transparent 50%);
background-size: 8px 100%;
background-repeat: repeat-x;
}
.legend-line.rw-write-acquired {
background-color: var(--link-acquired-write);
}
.legend-line.released {
background-color: var(--neutral-color);
background-image: linear-gradient(to right, var(--neutral-color) 70%, transparent 30%);
background-size: 10px 100%;
background-repeat: repeat-x;
}
.legend-line.spawn {
background-color: var(--link-spawn);
}
.legend-line.exit {
background-color: #34495e;
background-image: linear-gradient(to right, #34495e 50%, transparent 50%);
background-size: 8px 100%;
background-repeat: repeat-x;
}
.legend-line.notify {
background-color: var(--link-notify);
background-image: linear-gradient(to right, var(--link-notify) 40%, transparent 60%);
background-size: 6px 100%;
background-repeat: repeat-x;
}
.legend-line.deadlock {
background-color: var(--link-deadlock);
height: 5px;
}
footer {
margin-top: 5px;
background-color: var(--primary-color);
color: white;
padding: 12px;
border-radius: 8px;
text-align: center;
}
footer p {
margin-bottom: 8px;
font-size: 0.85rem;
}
.footer-links {
display: flex;
justify-content: center;
gap: 15px;
}
.footer-links a {
color: white;
text-decoration: none;
display: flex;
align-items: center;
gap: 6px;
padding: 6px 10px;
border-radius: 6px;
background-color: rgba(255, 255, 255, 0.15);
transition: background-color 0.1s;
}
.footer-links a:hover {
background-color: rgba(255, 255, 255, 0.25);
}
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: var(--color-modal-bg);
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-content {
background-color: var(--container-bg);
border-radius: 10px;
width: 90%;
max-width: 700px;
max-height: 90vh;
overflow-y: auto;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.3);
animation: modalShow 0.1s ease-out;
}
@keyframes modalShow {
from { opacity: 0; transform: translateY(-30px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes modalFadeIn {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}
@keyframes modalFadeOut {
from { opacity: 1; transform: scale(1); }
to { opacity: 0; transform: scale(0.95); }
}
.modal-fade-in {
animation: modalFadeIn 0.15s cubic-bezier(0.2, 0, 0, 1) forwards;
}
.modal-fade-out {
animation: modalFadeOut 0.1s cubic-bezier(0.2, 0, 0, 1) forwards;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 15px;
border-bottom: 1px solid var(--border-color);
}
.modal-header h2 {
color: var(--primary-color);
font-size: 1.4rem;
}
.modal-close {
background: none;
border: none;
font-size: 1.6rem;
cursor: pointer;
color: var(--neutral-color);
transition: color 0.1s;
}
.modal-close:hover {
color: var(--danger-color);
}
.modal-body {
padding: 15px;
line-height: 1.5;
}
.modal-body h3 {
color: var(--primary-color);
margin-top: 15px;
margin-bottom: 8px;
}
.modal-body ul {
margin-left: 15px;
margin-bottom: 12px;
}
.modal-body li {
margin-bottom: 4px;
}
.error-message {
color: var(--danger-color);
background-color: rgba(231, 76, 60, 0.1);
padding: 12px;
border-radius: 6px;
border-left: 4px solid var(--danger-color);
margin-top: 15px;
display: flex;
align-items: center;
gap: 8px;
}
.error-message i {
font-size: 1.3rem;
}
.error-message button {
margin-left: auto;
background-color: var(--danger-color);
padding: 4px 8px;
}
@media (max-width: 768px) {
.container {
padding: 8px;
}
header {
flex-direction: column;
gap: 10px;
text-align: center;
padding: 12px;
align-items: center;
}
.header-buttons {
width: 100%;
justify-content: center;
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.visualization-container {
flex-direction: column;
}
.visualization-left {
flex: 1;
}
.visualization-right {
flex: 1;
height: auto;
overflow-y: visible;
padding-right: 0;
padding-bottom: 0;
}
.visualization-right > div {
width: 100%;
}
.control-btn {
flex: 1;
min-width: 0;
font-size: 0.8rem;
padding: 6px 8px;
}
.control-btn span {
display: none;
}
.control-btn i {
margin-right: 0;
font-size: 1rem;
}
#graph {
height: 400px;
}
.legend-items {
justify-content: center;
}
.legend-item {
min-width: 0;
flex-basis: calc(50% - 8px);
}
.footer-links {
flex-direction: column;
gap: 8px;
align-items: center;
}
.footer-links a {
width: 100%;
justify-content: center;
}
.modal-content {
width: 95%;
}
}
@media (max-width: 460px) {
header {
flex-direction: column;
align-items: stretch;
gap: 8px;
}
.header-buttons {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 6px;
width: 100%;
}
.header-buttons .header-btn {
width: 100%;
min-width: 0;
justify-content: center;
box-sizing: border-box;
}
.logo {
justify-content: center;
width: 100%;
}
.logo h1 { width: 100%; text-align: center; }
}
svg {
width: 100%;
height: 100%;
max-height: 530px;
}
.node {
cursor: pointer;
transition: transform 0.1s ease;
}
.node text {
font-size: 13px;
font-weight: bold;
fill: white;
pointer-events: none;
user-select: none;
text-anchor: middle;
dominant-baseline: middle;
}
#drop-area {
border: 2px dashed var(--border-color);
border-radius: 8px;
padding: 25px;
text-align: center;
margin-bottom: 15px;
transition: all 0.1s;
background-color: var(--hover-bg);
}
#drop-area.highlight {
border-color: var(--primary-color);
background-color: rgba(52, 152, 219, 0.1);
}
#drop-area p {
margin: 8px 0;
color: var(--text-secondary);
}
#file-select-btn {
margin-top: 12px;
background-color: var(--primary-color);
color: white;
border: none;
padding: 8px 15px;
border-radius: 6px;
cursor: pointer;
font-size: 0.9rem;
display: inline-flex;
align-items: center;
min-width: 140px;
justify-content: center;
}
#file-select-btn:hover {
background-color: var(--primary-dark);
}
#upload-list {
margin-top: 15px;
}
.upload-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px;
border-radius: 6px;
margin-bottom: 8px;
background-color: var(--hover-bg);
border-left: 4px solid var(--primary-color);
}
a {
font-weight: bold;
text-decoration: none;
color: white;
}
a:hover{
text-decoration: underline;
}
button.simulation-btn {
background-color: var(--primary-color);
border: none;
color: white;
padding: 10px 15px;
border-radius: 5px;
cursor: pointer;
box-shadow: 0 2px 5px var(--shadow-color);
transition: all 0.1s ease;
}
button.primary-action {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
background-color: var(--primary-color);
color: white;
border: none;
padding: 12px 20px;
border-radius: 8px;
font-weight: 600;
cursor: pointer;
transition: all 0.1s ease;
}
.nav-link {
color: var(--text-color);
text-decoration: none;
padding: 0.5rem 1rem;
display: flex;
align-items: center;
gap: 0.5rem;
border-radius: 6px;
transition: background-color 0.1s, color 0.1s;
}
.mode-toggle {
cursor: pointer;
padding: 5px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
transition: background-color 0.1s;
}
.graph-node circle {
transition: stroke 0.1s, stroke-width 0.1s;
}
.settings-toggle {
cursor: pointer;
transition: transform 0.1s ease;
}
.settings-menu {
transition: all 0.1s;
}
.toast-container {
position: fixed;
bottom: 20px;
right: 20px;
max-width: 350px;
z-index: 9999;
display: flex;
flex-direction: column;
gap: 5px;
max-height: 80vh;
overflow-y: auto;
pointer-events: none;
}
.toast {
display: flex;
align-items: center;
padding: 10px 12px;
border-radius: 6px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
margin-bottom: 0;
transform: translateY(100px);
opacity: 0;
animation: toast-in 0.3s ease forwards;
background-color: #ffffff;
border-left: 4px solid var(--primary-color);
width: 100%;
box-sizing: border-box;
position: relative;
pointer-events: auto;
transition: all 0.2s ease;
cursor: pointer;
max-height: 60px;
overflow: hidden;
margin-top: -30px;
}
.toast:first-child {
margin-top: 0;
}
.toast:hover {
margin-top: 0;
margin-bottom: 5px;
z-index: 10000;
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
}
.toast.expanded {
max-height: 200px;
margin-top: 0;
margin-bottom: 5px;
z-index: 10000;
}
[data-theme="dark"] .toast {
background-color: #2c3e50;
}
.toast-success {
border-left: 4px solid var(--success-color);
}
.toast-error {
border-left: 4px solid var(--danger-color);
}
.toast-warning {
border-left: 4px solid var(--warning-color);
}
.toast-info {
border-left: 4px solid var(--primary-color);
}
.toast-icon {
margin-right: 12px;
font-size: 18px;
flex-shrink: 0;
}
.toast-success .toast-icon {
color: var(--success-color);
}
.toast-error .toast-icon {
color: var(--danger-color);
}
.toast-warning .toast-icon {
color: var(--warning-color);
}
.toast-info .toast-icon {
color: var(--primary-color);
}
.toast-content {
flex: 1;
color: #333333;
font-size: 14px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.toast.expanded .toast-content {
white-space: normal;
text-overflow: initial;
}
[data-theme="dark"] .toast-content {
color: #f5f5f5;
}
.toast-close {
color: #6c757d;
background: none;
border: none;
font-size: 16px;
cursor: pointer;
padding: 0;
margin-left: 8px;
opacity: 0.7;
transition: opacity 0.2s;
flex-shrink: 0;
z-index: 10001;
}
[data-theme="dark"] .toast-close {
color: #adb5bd;
}
.toast-close:hover {
opacity: 1;
}
@keyframes toast-in {
from {
transform: translateY(100px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
@keyframes toast-out {
from {
transform: translateY(0);
opacity: 1;
}
to {
transform: translateY(-20px);
opacity: 0;
}
}
@media (max-width: 480px) {
.toast-container {
bottom: 10px;
left: 10px;
right: 10px;
max-width: none;
}
.toast {
padding: 8px 10px;
}
}
.cycle-visualization {
padding: 12px;
background-color: var(--hover-bg);
border-radius: 8px;
margin: 12px 0;
text-align: center;
border-left: 4px solid var(--danger-color);
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
gap: 8px;
}
.cycle-visualization i {
color: var(--danger-color);
}
.logo img {
height: 32px;
width: auto;
margin-right: 10px;
filter: brightness(0) invert(1);
}
@media (max-width: 768px) {
.logo img {
height: 24px;
}
}
.cycle-visualization i.fa-long-arrow-alt-right {
animation: arrowPulse 1s infinite;
}
@keyframes arrowPulse {
0% { transform: translateX(0); opacity: 0.6; }
50% { transform: translateX(3px); opacity: 1; }
100% { transform: translateX(0); opacity: 0.6; }
}
.cycle-visualization.animate__pulse {
animation-iteration-count: infinite;
animation-duration: 2s;
}
.resource-list {
margin-left: 15px;
padding-left: 10px;
border-left: 3px solid var(--primary-color);
}
.resource-list li {
margin-bottom: 5px;
}
.thread-id {
font-family: 'Roboto Mono', monospace;
color: var(--thread-fill);
font-weight: 700;
}
.resource-id {
font-family: 'Roboto Mono', monospace;
color: var(--primary-color);
font-weight: 500;
}
#step-info p {
line-height: 1.5;
margin: 8px 0;
}
strong {
color: var(--highlight-color, #e74c3c);
}
.timestamp {
font-size: 0.85rem;
color: var(--text-secondary-color, #777);
margin-top: 15px;
border-top: 1px dotted var(--border-color, #ddd);
padding-top: 8px;
}
.timestamp i {
margin-right: 5px;
}
.legend-container {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin: 15px 0;
}
.legend-item-help {
display: flex;
align-items: center;
background-color: var(--hover-bg);
padding: 8px 12px;
border-radius: 6px;
flex: 1 1 300px;
transition: all 0.2s ease;
}
.legend-item-help:hover {
transform: translateY(-2px);
box-shadow: 0 3px 10px rgba(0,0,0,0.1);
}
.legend-item-help .legend-color,
.legend-item-help .legend-line {
margin-right: 10px;
flex-shrink: 0;
}
.legend-item-help span {
font-size: 0.95rem;
}
.legend-item-help span strong {
color: var(--primary-color);
}
@media (max-width: 768px) {
.legend-item-help {
flex: 1 1 100%;
padding: 6px 10px;
}
.legend-container {
gap: 8px;
}
}
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.5);
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.15s ease;
}
.modal.active {
opacity: 1;
display: flex;
}
.modal-content {
background-color: var(--bg-color);
margin: 10px;
padding: 0;
border-radius: 8px;
max-width: 800px;
width: 90%;
max-height: 90vh;
overflow-y: auto;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
transform: translateY(-20px);
opacity: 0;
transition: transform 0.2s ease, opacity 0.2s ease;
}
.modal-content.show {
transform: translateY(0);
opacity: 1;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px 20px;
border-bottom: 1px solid var(--border-color);
}
.modal-header h2 {
margin: 0;
font-size: 1.25rem;
}
.modal-close {
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
color: var(--text-secondary-color);
transition: all 0.2s ease;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
}
.modal-close:hover {
color: var(--danger-color);
background-color: var(--hover-bg);
transform: scale(1.1);
}
.modal-body {
padding: 20px;
}
@media (max-width: 768px) {
.modal-content {
width: 95%;
margin: 5px;
max-height: 95vh;
}
.modal-header {
padding: 12px 15px;
}
.modal-body {
padding: 15px;
}
}
.deadlock-line {
margin: 12px 0;
line-height: 2.0;
}
.thread-id { margin: 0 2px; }
.resource-id { margin: 0 2px; }