Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{".":"0.5.0"}
{".":"0.5.1"}
13 changes: 10 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,18 @@ services:
container_name: cashflow
ports:
- "8080:80"
# Skip entrypoint template processing (fails on read-only root)
entrypoint: ["nginx", "-g", "daemon off;"]
# Mount local sources for live debugging without rebuild
volumes:
- ./index.html:/usr/share/nginx/html/index.html:ro
- ./src:/usr/share/nginx/html/src:ro
read_only: true
tmpfs:
- /tmp
- /var/cache/nginx
- /var/run
# nginx user (uid/gid 101) must own cache/run dirs
- /tmp:uid=101,gid=101,mode=1777
- /var/cache/nginx:uid=101,gid=101,mode=1777
- /var/run:uid=101,gid=101,mode=1777
security_opt:
- no-new-privileges:true
deploy:
Expand Down
71 changes: 29 additions & 42 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ <h1 class="text-2xl font-bold">Cashflow Simulator</h1>
</p>
</div>
<div class="flex items-center gap-3">
<span class="text-sm text-blue-200">v0.5.0</span>
<span class="text-sm text-blue-200">v0.5.1</span>
<button
@click="toggleTheme"
class="text-white hover:text-blue-200 text-2xl"
Expand Down Expand Up @@ -1049,6 +1049,32 @@ <h2 class="text-lg font-semibold text-gray-700 dark:text-gray-200">
const expenseData = chartData.map(r => (r.cashflow < 0 ? r.cashflow : null));
const balanceData = chartData.map(r => r.balance);

const AXIS_ORANGE = '#FF4F00';
// Must be attached via config.plugins (array); options.plugins only configures registered plugins.
// Attach after JSON clones — stringify strips functions.
const axisHighlightPlugin = {
id: 'axisHighlight',
afterDraw(chart) {
const yScale = chart.scales.y;
const ctx = chart.ctx;
const { left, right, top, bottom } = chart.chartArea;
// Only highlight y=0 — not the chart-area baseline (scale min)
const zeroY = yScale.getPixelForValue(0);
if (zeroY < top || zeroY > bottom) {
return;
}
ctx.save();
ctx.strokeStyle = AXIS_ORANGE;
ctx.lineWidth = 2.5;
ctx.setLineDash([]);
ctx.beginPath();
ctx.moveTo(left, zeroY);
ctx.lineTo(right, zeroY);
ctx.stroke();
ctx.restore();
},
};

const chartOptions = {
responsive: true,
maintainAspectRatio: false,
Expand All @@ -1061,45 +1087,6 @@ <h2 class="text-lg font-semibold text-gray-700 dark:text-gray-200">
display: true,
position: 'top',
},
// Draw a stronger/brighter horizontal line at y=0
zeroLine: {
afterDraw: chart => {
const yScale = chart.scales.y;
const ctx = chart.ctx;
const { top, bottom } = chart.chartArea;
const xLeft = chart.chartArea.left;
const xRight = chart.chartArea.right;
const zeroY = yScale.getPixelForValue(0);
if (zeroY >= top && zeroY <= bottom) {
ctx.save();
ctx.strokeStyle = darkMode.value ? '#e5e7eb' : '#374151';
ctx.lineWidth = 2.5;
ctx.setLineDash([]);
ctx.beginPath();
ctx.moveTo(xLeft, zeroY);
ctx.lineTo(xRight, zeroY);
ctx.stroke();
ctx.restore();
}
},
},
// Draw a stronger x-axis baseline
xAxisLine: {
afterDraw: chart => {
const ctx = chart.ctx;
const { left, right, bottom } = chart.chartArea;
const xBottomY = bottom;
ctx.save();
ctx.strokeStyle = darkMode.value ? '#e5e7eb' : '#374151';
ctx.lineWidth = 2.5;
ctx.setLineDash([]);
ctx.beginPath();
ctx.moveTo(left, xBottomY);
ctx.lineTo(right, xBottomY);
ctx.stroke();
ctx.restore();
},
},
},
scales: {
x: {
Expand Down Expand Up @@ -1156,6 +1143,7 @@ <h2 class="text-lg font-semibold text-gray-700 dark:text-gray-200">
inlineConfig.data.datasets[0].categoryPercentage = 0.5;
inlineConfig.data.datasets[1].barPercentage = 1.0;
inlineConfig.data.datasets[1].categoryPercentage = 0.5;
inlineConfig.plugins = [axisHighlightPlugin];
cashflowChartInstance = new Chart(cashflowChart.value, inlineConfig);
}
if (cashflowChartFullscreen.value) {
Expand All @@ -1180,8 +1168,6 @@ <h2 class="text-lg font-semibold text-gray-700 dark:text-gray-200">
position: 'top',
labels: { color: textColor },
},
zeroLine: chartOptions.plugins.zeroLine,
xAxisLine: chartOptions.plugins.xAxisLine,
};
const fullscreenConfig = JSON.parse(JSON.stringify(chartConfig));
// Set bar spacing per dataset for fullscreen
Expand All @@ -1190,6 +1176,7 @@ <h2 class="text-lg font-semibold text-gray-700 dark:text-gray-200">
fullscreenConfig.data.datasets[1].barPercentage = 1.0;
fullscreenConfig.data.datasets[1].categoryPercentage = 0.5;
fullscreenConfig.options = fullscreenOptions;
fullscreenConfig.plugins = [axisHighlightPlugin];
cashflowChartFullscreenInstance = new Chart(
cashflowChartFullscreen.value,
fullscreenConfig
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cf-sim",
"version": "0.5.0",
"version": "0.5.1",
"description": "Visualize your income, expenses, and balance over time",
"type": "module",
"scripts": {
Expand Down
28 changes: 11 additions & 17 deletions src/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,6 @@
color: inherit;
}

/* Page background — desk photo from aloisio.dev */
body {
background-color: #226e93;
background-image: url('../assets/bg-desk.jpg');
background-size: cover;
background-position: center center;
background-repeat: no-repeat;
background-attachment: fixed;
}

/* CSS custom properties for theming — dark mode via .dark class */
:root {
--color-bg: #f3f4f6;
Expand Down Expand Up @@ -102,14 +92,8 @@ html.dark {

/* Apply CSS variables to all relevant elements in dark mode */
html.dark body {
background-color: var(--color-bg) !important;
color: var(--color-text) !important;
background-color: #226e93 !important;
background-image:
linear-gradient(rgba(17, 24, 39, 0.55), rgba(17, 24, 39, 0.55)), url('../assets/bg-desk.jpg') !important;
background-size: cover !important;
background-position: center center !important;
background-repeat: no-repeat !important;
background-attachment: fixed !important;
}
html.dark header {
background-color: var(--color-header-bg) !important;
Expand All @@ -136,9 +120,19 @@ html.dark .bg-gray-800 {
html.dark .bg-blue-50 {
background-color: var(--color-hover-bg) !important;
}
html.dark .bg-blue-100 {
background-color: var(--color-hover-bg) !important;
}
html.dark .bg-blue-900 {
background-color: var(--color-hover-bg) !important;
}
/* Tailwind hover:bg-blue-100 stays light unless overridden — washes out green/red values */
html.dark .hover\:bg-blue-100:hover {
background-color: var(--color-hover-bg) !important;
}
html.dark .hover\:bg-gray-200:hover {
background-color: #374151 !important;
}
html.dark .bg-red-50 {
background-color: var(--color-error-bg) !important;
}
Expand Down