Skip to content

Commit c482340

Browse files
committed
Fix PlantUML start/end state colors in dark mode
- Start states: orange filled circle (fill + stroke) - End states: outer circle with orange stroke only (transparent fill), inner circle with orange fill - Updated JavaScript to detect nested polygons and apply correct styling - Updated CSS comments to reflect the differentiation logic
1 parent 610e2f7 commit c482340

File tree

4 files changed

+139
-1
lines changed

4 files changed

+139
-1
lines changed

docs/diagrams/include/themes/dark.puml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,5 +57,13 @@ skinparam state {
5757
EndColor #ffffff
5858
}
5959

60+
' Explicitly set start/end state colors for visibility in dark mode
61+
' Note: PlantUML may hardcode start/end state colors, so these may not work
6062
skinparam StateStartColor #ffffff
6163
skinparam StateEndColor #ffffff
64+
skinparam StateStartBackgroundColor #ffffff
65+
skinparam StateEndBackgroundColor #ffffff
66+
skinparam StateStartBorderColor #ffffff
67+
skinparam StateEndBorderColor #ffffff
68+
skinparam StateStartFillColor #ffffff
69+
skinparam StateEndFillColor #ffffff

docs/diagrams/src/uctm-process.puml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
!option handwritten false
2-
@startuml Plan Build Run
2+
@startuml
33
!include ../include/themes/light.puml
44

55
state Planxs {

docs/javascripts/darkable-objects.js

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,118 @@
7979
svg.removeAttribute('height');
8080
}
8181

82+
function fixStartEndStateColors(svg) {
83+
if (!isDarkMode()) return;
84+
// Fix hardcoded #222222 fill/stroke colors for start/end states in dark mode
85+
// Start state: one filled circle (orange fill + stroke)
86+
// End state: smaller filled circle inside larger non-filled circle (outer: orange stroke only, inner: orange fill)
87+
88+
// Helper to get bounding box of a polygon
89+
function getBoundingBox(polygon) {
90+
const points = polygon.getAttribute('points');
91+
if (!points) return null;
92+
const coords = points.split(/[\s,]+/).map(parseFloat).filter(n => !isNaN(n));
93+
if (coords.length < 4) return null;
94+
95+
let minX = coords[0], maxX = coords[0];
96+
let minY = coords[1], maxY = coords[1];
97+
for (let i = 2; i < coords.length; i += 2) {
98+
minX = Math.min(minX, coords[i]);
99+
maxX = Math.max(maxX, coords[i]);
100+
minY = Math.min(minY, coords[i + 1]);
101+
maxY = Math.max(maxY, coords[i + 1]);
102+
}
103+
return { minX, maxX, minY, maxY, width: maxX - minX, height: maxY - minY };
104+
}
105+
106+
// Helper to check if two bounding boxes overlap significantly (indicating nested circles)
107+
function areNested(bb1, bb2, threshold = 0.7) {
108+
if (!bb1 || !bb2) return false;
109+
const center1 = { x: (bb1.minX + bb1.maxX) / 2, y: (bb1.minY + bb1.maxY) / 2 };
110+
const center2 = { x: (bb2.minX + bb2.maxX) / 2, y: (bb2.minY + bb2.maxY) / 2 };
111+
const distance = Math.sqrt(Math.pow(center1.x - center2.x, 2) + Math.pow(center1.y - center2.y, 2));
112+
const avgSize = (bb1.width + bb1.height + bb2.width + bb2.height) / 4;
113+
return distance < avgSize * threshold;
114+
}
115+
116+
// Find all polygons with dark fill/stroke (start/end states)
117+
const candidatePolygons = Array.from(svg.querySelectorAll('polygon')).filter((polygon) => {
118+
const fill = polygon.getAttribute('fill');
119+
const style = polygon.getAttribute('style') || '';
120+
const hasDarkFill = fill === '#222222' || fill === '#222';
121+
const hasDarkStroke = style.includes('stroke:#222222') || style.includes('stroke:#222');
122+
return hasDarkFill || hasDarkStroke;
123+
});
124+
125+
// Group polygons that are nested (end states have two circles)
126+
const processed = new Set();
127+
candidatePolygons.forEach((polygon) => {
128+
if (processed.has(polygon)) return;
129+
130+
const bb1 = getBoundingBox(polygon);
131+
if (!bb1) return;
132+
133+
// Look for a nearby polygon that might be the other circle of an end state
134+
let nestedPolygon = null;
135+
for (const other of candidatePolygons) {
136+
if (other === polygon || processed.has(other)) continue;
137+
const bb2 = getBoundingBox(other);
138+
if (bb2 && areNested(bb1, bb2)) {
139+
nestedPolygon = other;
140+
break;
141+
}
142+
}
143+
144+
if (nestedPolygon) {
145+
// This is an end state: two nested circles
146+
processed.add(polygon);
147+
processed.add(nestedPolygon);
148+
149+
const bb2 = getBoundingBox(nestedPolygon);
150+
const isOuter = bb1.width > bb2.width || bb1.height > bb2.height;
151+
const outer = isOuter ? polygon : nestedPolygon;
152+
const inner = isOuter ? nestedPolygon : polygon;
153+
154+
// Outer circle: orange stroke, transparent fill
155+
const outerStyle = outer.getAttribute('style') || '';
156+
outer.setAttribute('fill', 'transparent');
157+
outer.setAttribute('stroke', '#ff6f00');
158+
if (outerStyle) {
159+
outer.setAttribute('style', outerStyle
160+
.replace(/stroke:#222222/g, 'stroke:#ff6f00')
161+
.replace(/stroke:#222([^0-9])/g, 'stroke:#ff6f00$1')
162+
.replace(/fill:#222222/g, 'fill:transparent')
163+
.replace(/fill:#222([^0-9])/g, 'fill:transparent$1'));
164+
}
165+
166+
// Inner circle: orange fill and stroke
167+
const innerStyle = inner.getAttribute('style') || '';
168+
inner.setAttribute('fill', '#ff6f00');
169+
inner.setAttribute('stroke', '#ff6f00');
170+
if (innerStyle) {
171+
inner.setAttribute('style', innerStyle
172+
.replace(/stroke:#222222/g, 'stroke:#ff6f00')
173+
.replace(/stroke:#222([^0-9])/g, 'stroke:#ff6f00$1')
174+
.replace(/fill:#222222/g, 'fill:#ff6f00')
175+
.replace(/fill:#222([^0-9])/g, 'fill:#ff6f00$1'));
176+
}
177+
} else {
178+
// This is a start state: single filled circle
179+
processed.add(polygon);
180+
const style = polygon.getAttribute('style') || '';
181+
polygon.setAttribute('fill', '#ff6f00');
182+
polygon.setAttribute('stroke', '#ff6f00');
183+
if (style) {
184+
polygon.setAttribute('style', style
185+
.replace(/stroke:#222222/g, 'stroke:#ff6f00')
186+
.replace(/stroke:#222([^0-9])/g, 'stroke:#ff6f00$1')
187+
.replace(/fill:#222222/g, 'fill:#ff6f00')
188+
.replace(/fill:#222([^0-9])/g, 'fill:#ff6f00$1'));
189+
}
190+
}
191+
});
192+
}
193+
82194
function renderDiagram(diagram) {
83195
const markup =
84196
(isDarkMode() ? diagram.darkMarkup : diagram.lightMarkup) ||
@@ -89,6 +201,7 @@
89201
if (svg) {
90202
diagram.naturalWidth = getNaturalWidth(svg) || diagram.naturalWidth;
91203
applySizing(diagram, svg);
204+
fixStartEndStateColors(svg);
92205
}
93206
}
94207

docs/stylesheets/extra.css

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,23 @@
680680
}
681681

682682
/* Hover state - orange on hover for all tabs */
683+
684+
/* Fix PlantUML start/end state visibility in dark mode */
685+
/* JavaScript (darkable-objects.js) handles the differentiation:
686+
* - Start states: orange fill + stroke
687+
* - End states: outer circle (transparent fill + orange stroke), inner circle (orange fill + stroke)
688+
* CSS here is a fallback to ensure visibility before JS runs */
689+
[data-md-color-scheme="slate"] svg[data-diagram-type="STATE"] polygon[fill="#222222"],
690+
[data-md-color-scheme="slate"] svg[data-diagram-type="STATE"] polygon[fill="#222"],
691+
[data-md-color-scheme="slate"] .ekgf-diagram svg[data-diagram-type="STATE"] polygon[fill="#222222"],
692+
[data-md-color-scheme="slate"] .ekgf-diagram svg[data-diagram-type="STATE"] polygon[fill="#222"],
693+
[data-md-color-scheme="slate"] svg[data-diagram-type="STATE"] polygon[style*="stroke:#222222"],
694+
[data-md-color-scheme="slate"] svg[data-diagram-type="STATE"] polygon[style*="stroke:#222"],
695+
[data-md-color-scheme="slate"] .ekgf-diagram svg[data-diagram-type="STATE"] polygon[style*="stroke:#222222"],
696+
[data-md-color-scheme="slate"] .ekgf-diagram svg[data-diagram-type="STATE"] polygon[style*="stroke:#222"] {
697+
stroke: #ff6f00 !important;
698+
/* Fill will be handled by JavaScript to differentiate start vs end states */
699+
}
683700
.md-typeset .tabbed-labels > label:hover {
684701
color: #ff6f00 !important;
685702
}

0 commit comments

Comments
 (0)