215 lines
11 KiB
HTML
215 lines
11 KiB
HTML
<!DOCTYPE html>
|
|
<html data-bs-theme="light" lang="en-au">
|
|
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
|
<title>Tools | Nathan.Woodburn/</title>
|
|
<meta name="theme-color" content="#000000">
|
|
<link rel="canonical" href="https://nathan.woodburn.au/tools">
|
|
<meta property="og:url" content="https://nathan.woodburn.au/tools">
|
|
<meta name="fediverse:creator" content="@nathanwoodburn@mastodon.woodburn.au">
|
|
<meta name="twitter:description" content="G'day, this is my personal website. You can find out about me or check out some of my projects.">
|
|
<meta property="og:title" content="Nathan.Woodburn/">
|
|
<meta name="twitter:card" content="summary">
|
|
<meta name="twitter:image" content="https://nathan.woodburn.au/assets/img/profile.jpg">
|
|
<meta property="og:type" content="website">
|
|
<meta name="twitter:title" content="Nathan.Woodburn/">
|
|
<meta property="og:description" content="G'day, this is my personal website. You can find out about me or check out some of my projects.">
|
|
<meta property="og:image" content="https://nathan.woodburn.au/assets/img/profile.jpg">
|
|
<meta name="description" content="Check out some tools I use">
|
|
<link rel="apple-touch-icon" type="image/png" sizes="180x180" href="/assets/img/favicon/apple-touch-icon.png">
|
|
<link rel="icon" type="image/png" sizes="16x16" href="/assets/img/favicon/favicon-16x16.png">
|
|
<link rel="icon" type="image/png" sizes="32x32" href="/assets/img/favicon/favicon-32x32.png">
|
|
<link rel="icon" type="image/png" sizes="180x180" href="/assets/img/favicon/apple-touch-icon.png">
|
|
<link rel="icon" type="image/png" sizes="192x192" href="/assets/img/favicon/android-chrome-192x192.png">
|
|
<link rel="icon" type="image/png" sizes="512x512" href="/assets/img/favicon/android-chrome-512x512.png">
|
|
<link rel="stylesheet" href="/assets/bootstrap/css/bootstrap.min.css">
|
|
<link rel="manifest" href="/manifest.json" crossorigin="use-credentials">
|
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic&display=swap">
|
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Cabin:700&display=swap">
|
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Anonymous+Pro&display=swap">
|
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap">
|
|
<link rel="stylesheet" href="/assets/fonts/font-awesome.min.css">
|
|
<link rel="stylesheet" href="/assets/css/styles.min.css">
|
|
<link rel="stylesheet" href="/assets/css/brand-reveal.min.css">
|
|
<link rel="stylesheet" href="/assets/css/profile.min.css">
|
|
<link rel="stylesheet" href="/assets/css/Social-Icons.min.css">
|
|
<link rel="stylesheet" href="/assets/css/tools.min.css">
|
|
<link rel="me" href="https://mastodon.woodburn.au/@nathanwoodburn" />
|
|
<script async src="https://umami.woodburn.au/script.js" data-website-id="6a55028e-aad3-481c-9a37-3e096ff75589"></script>
|
|
</head>
|
|
|
|
<body id="page-top" data-bs-spy="scroll" data-bs-target="#mainNav" data-bs-offset="77">
|
|
<nav class="navbar navbar-expand-md fixed-top navbar-light" id="mainNav" style="background: var(--bs-navbar-hover-color);">
|
|
<div class="container-fluid"><a class="navbar-brand" href="/#">
|
|
<div style="padding-right: 1em;display: inline-flex;">
|
|
<div class="slider"><span>/</span></div><span class="brand">Nathan.Woodburn</span>
|
|
</div>
|
|
</a><button data-bs-toggle="collapse" class="navbar-toggler navbar-toggler-right" data-bs-target="#navbarResponsive" type="button" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation" value="Menu"><i class="fa fa-bars"></i></button>
|
|
<div class="collapse navbar-collapse" id="navbarResponsive">
|
|
<ul class="navbar-nav ms-auto">
|
|
<li class="nav-item nav-link"><a class="nav-link" href="/">Home</a></li>
|
|
<li class="nav-item nav-link"><a class="nav-link" href="/hosting">Hosting</a></li>
|
|
<li class="nav-item nav-link"><a class="nav-link" href="/projects">Projects</a></li>
|
|
<li class="nav-item nav-link"><a class="nav-link" href="/tools">Tools</a></li>
|
|
<li class="nav-item nav-link"><a class="nav-link" href="/blog">Blog</a></li>
|
|
<li class="nav-item nav-link"><a class="nav-link" href="/now">Now</a></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
<header class="masthead" style="background: url("/assets/img/bg/projects.webp") bottom / cover no-repeat;height: auto;padding-top: 20px;">
|
|
<div style="margin-top: 150px;margin-bottom: 100px;">
|
|
<div class="container">
|
|
<div class="row">
|
|
<div class="col-lg-8 mx-auto">
|
|
<h1 class="brand-heading">Tools</h1>
|
|
<p>Here is a list of applications, tools and services I use regularly.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
<section class="text-center content-section" id="tools" style="padding-bottom: 100px;">
|
|
<div class="container">{% for type, tools_in_type in tools | groupby('type') %}
|
|
<h2 class="mt-4 mb-3 sticky-top bg-primary py-2 section-header" id="{{type}}">{{ type }}</h2>
|
|
<div class="row">
|
|
{% for tool in tools_in_type %}
|
|
<div class="col-md-6 col-lg-4 mb-4">
|
|
<div class="card h-100 shadow-sm transition-all" style="transition: transform 0.2s, box-shadow 0.2s;">
|
|
<div class="card-body d-flex flex-column">
|
|
<h4 class="card-title">{{tool.name}}</h4>
|
|
<p class="card-text">{{ tool.description }}</p>
|
|
<div class="btn-group gap-3 mt-auto" role="group">{% if tool.demo %}<button class="btn btn-primary"
|
|
type="button" data-bs-target="#modal-{{tool.name}}" data-bs-toggle="modal"
|
|
style="transition: transform 0.2s, background-color 0.2s;">View Demo</button>{% endif %}<a
|
|
class="btn btn-primary" role="button" href="{{tool.url}}" target="_blank"
|
|
style="transition: transform 0.2s, background-color 0.2s;">{{tool.name}} Website</a></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
<!-- Modals for this type -->
|
|
{% for tool in tools_in_type %}
|
|
{% if tool.demo %}
|
|
<div id="modal-{{tool.name}}" class="modal fade" role="dialog" tabindex="-1" style="z-index: 1055;"
|
|
data-demo-url="{{ tool.demo_url | e }}">
|
|
<div class="modal-dialog modal-xl" role="document">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h4 class="modal-title">{{tool.name}}</h4><button class="btn-close" type="button" aria-label="Close"
|
|
data-bs-dismiss="modal"></button>
|
|
</div>
|
|
|
|
<div class="modal-body" data-demo-loaded="false"></div>
|
|
</div>
|
|
<div class="modal-footer"><button class="btn btn-light" type="button" data-bs-dismiss="modal">Close</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
{% endfor %}
|
|
{% endfor %}
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
const navbar = document.getElementById('mainNav');
|
|
const headers = document.querySelectorAll('.section-header');
|
|
|
|
if (navbar) {
|
|
const navbarHeight = navbar.offsetHeight;
|
|
headers.forEach(header => {
|
|
header.style.top = navbarHeight + 'px';
|
|
header.style.zIndex = '100';
|
|
header.style.scrollMarginTop = navbarHeight + 'px';
|
|
});
|
|
|
|
// Handle hash navigation on page load
|
|
if (window.location.hash) {
|
|
setTimeout(() => {
|
|
const target = document.querySelector(window.location.hash);
|
|
if (target) {
|
|
window.scrollTo({
|
|
top: target.offsetTop - navbarHeight,
|
|
behavior: 'smooth'
|
|
});
|
|
}
|
|
}, 0);
|
|
}
|
|
}
|
|
|
|
// Load demo in modal
|
|
document.querySelectorAll('.modal').forEach(modal => {
|
|
modal.addEventListener('show.bs.modal', () => {
|
|
const body = modal.querySelector('.modal-body');
|
|
if (body.dataset.demoLoaded === 'false') {
|
|
const demoUrl = modal.dataset.demoUrl;
|
|
const iframeId = 'iframe-' + modal.id;
|
|
|
|
// Add a div on top of all content to show loading message
|
|
const loadingDiv = document.createElement('div');
|
|
loadingDiv.style.position = 'absolute';
|
|
loadingDiv.style.top = '0';
|
|
loadingDiv.style.left = '0';
|
|
loadingDiv.style.width = '100%';
|
|
loadingDiv.style.height = '100%';
|
|
loadingDiv.style.backgroundColor = 'rgb(0, 0, 0)';
|
|
loadingDiv.style.display = 'flex';
|
|
loadingDiv.style.justifyContent = 'center';
|
|
loadingDiv.style.alignItems = 'center';
|
|
loadingDiv.style.zIndex = '10';
|
|
const loadingMsg = document.createElement('p');
|
|
loadingMsg.className = 'text-center';
|
|
loadingMsg.textContent = 'Loading demo...';
|
|
loadingDiv.appendChild(loadingMsg);
|
|
body.style.position = 'relative';
|
|
body.appendChild(loadingDiv);
|
|
|
|
// Create iframe
|
|
const iframe = document.createElement('iframe');
|
|
iframe.src = demoUrl + '/iframe';
|
|
iframe.id = iframeId;
|
|
iframe.style.width = '100%';
|
|
iframe.style.height = '400px'; // temporary height
|
|
iframe.style.border = '0';
|
|
iframe.setAttribute('scrolling', 'no');
|
|
iframe.setAttribute('allowfullscreen', 'true');
|
|
|
|
body.appendChild(iframe);
|
|
body.dataset.demoLoaded = 'true';
|
|
|
|
// Listen for bodySize message from asciinema iframe
|
|
const origin = new URL(demoUrl).origin;
|
|
function onMessage(event) {
|
|
if (event.origin !== origin || event.source !== iframe.contentWindow) return;
|
|
if (event.data.type === 'bodySize' && event.data.payload.height) {
|
|
iframe.style.height = event.data.payload.height + 'px';
|
|
// Remove loading message
|
|
body.removeChild(loadingDiv);
|
|
// Optional: limit modal max height
|
|
modal.querySelector('.modal-dialog').style.maxHeight = '90vh';
|
|
}
|
|
}
|
|
|
|
window.addEventListener('message', onMessage, false);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
</script></div>
|
|
</section>
|
|
<footer>
|
|
<div class="container text-center">
|
|
<p class="copyright">Copyright © Nathan.Woodburn/ 2025</p>
|
|
</div>
|
|
</footer>{{handshake_scripts | safe}}
|
|
<script src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
|
<script src="/assets/js/script.min.js"></script>
|
|
<script src="/assets/js/grayscale.min.js"></script>
|
|
<script src="/assets/js/hacker.min.js"></script>
|
|
</body>
|
|
|
|
</html> |