mirror of
https://github.com/visioncortex/vtracer.git
synced 2026-09-13 15:45:47 -07:00
1adde15f42
publicDownloadUrl built the disk image URL from manifest.version, but the version carries a build suffix the release tag does not: version "1.0.0-alpha.3.app.59" lives under tag "1.0.0-alpha.3". Every macOS visitor was handed a 404. Derive it from the updater URL instead, rewriting only the filename so the tag comes from the release directory the manifest points at.
479 lines
20 KiB
HTML
Generated
479 lines
20 KiB
HTML
Generated
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>VTracer</title>
|
|
<link rel="apple-touch-icon" sizes="192x192" href="./assets/apple-icon.png">
|
|
<link rel="icon" type="image/png" sizes="192x192" href="./assets/apple-icon.png">
|
|
<!-- UIkit CSS -->
|
|
<link rel="stylesheet" href="./uikit/css/uikit.min.css" />
|
|
|
|
<!-- UIkit JS -->
|
|
<script src="./uikit/js/uikit.min.js"></script>
|
|
<script src="./uikit/js/uikit-icons.min.js"></script>
|
|
<!-- Google tag (gtag.js) -->
|
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XPL1JGMDSC"></script>
|
|
<script>
|
|
window.dataLayer = window.dataLayer || [];
|
|
function gtag(){dataLayer.push(arguments);}
|
|
gtag('js', new Date());
|
|
|
|
gtag('config', 'G-XPL1JGMDSC');
|
|
</script>
|
|
<style>
|
|
html, body {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
.uk-dark body,
|
|
.uk-dark .container {
|
|
background: #fff;
|
|
}
|
|
.uk-dark #vc-logo-light {
|
|
display: none;
|
|
}
|
|
.uk-light #vc-logo {
|
|
display: none;
|
|
}
|
|
.uk-light body,
|
|
.uk-light .container,
|
|
.uk-light .uk-modal-dialog {
|
|
background: #2e2e2e;
|
|
}
|
|
@media screen and (prefers-color-scheme: dark) {
|
|
html {
|
|
background: #2e2e2e;
|
|
}
|
|
}
|
|
#droptext {
|
|
background: #00275D;
|
|
border: 2px dashed #CC972E;
|
|
}
|
|
#droptext.hovering {
|
|
border: 2px dashed #fff;
|
|
}
|
|
.uk-navbar-right.uk-padding-small {
|
|
padding-top: 0;
|
|
padding-bottom: 0;
|
|
}
|
|
#export,
|
|
.uk-button-group .uk-button.selected {
|
|
color: #fff;
|
|
background: #00275D;
|
|
border-color: #CC972E;
|
|
}
|
|
.uk-dark #export,
|
|
.uk-dark .uk-button-group .uk-button.selected {
|
|
border-width: 2px;
|
|
}
|
|
.uk-dark #droptext {
|
|
border-width: 4px;
|
|
}
|
|
.uk-button-group > button {
|
|
padding-left: 10px;
|
|
padding-right: 10px;
|
|
}
|
|
@media only screen and (min-width: 1280px) {
|
|
.uk-button-group > button {
|
|
padding-left: 25px;
|
|
padding-right: 25px;
|
|
}
|
|
}
|
|
#canvas-container {
|
|
position: relative;
|
|
height: max-content;
|
|
}
|
|
#svg, #frame {
|
|
position: absolute;
|
|
width: 100%;
|
|
margin-bottom: 50px;
|
|
}
|
|
#svg > path:hover {
|
|
stroke: #ff0;
|
|
}
|
|
#droptext {
|
|
height: 480px;
|
|
color: #CC972E;
|
|
}
|
|
input[type=range]::-webkit-slider-thumb {
|
|
-webkit-appearance: none;
|
|
background: #00275D;
|
|
border-color: #CC972E;
|
|
}
|
|
input[type=range]::-moz-range-thumb {
|
|
background: #00275D;
|
|
border-color: #CC972E;
|
|
}
|
|
input[type=range]::-ms-thumb {
|
|
background: #00275D;
|
|
border-color: #CC972E;
|
|
}
|
|
#drop, #gallery, #options {
|
|
border-top: 1px solid #686868;
|
|
}
|
|
#gallery {
|
|
padding-bottom: 0;
|
|
}
|
|
.galleryitem {
|
|
cursor: pointer;
|
|
padding: 2px;
|
|
margin: 1px;
|
|
}
|
|
.galleryitem:hover {
|
|
padding: 0px;
|
|
margin: 0px;
|
|
border: 2px solid #CC972E;
|
|
}
|
|
.galleryitem img {
|
|
max-height: 120px;
|
|
}
|
|
.uk-text-meta {
|
|
font-size: 12px;
|
|
}
|
|
.desktop-banner {
|
|
border: 1px solid rgba(204, 151, 46, 0.55);
|
|
background: rgba(204, 151, 46, 0.12);
|
|
gap: 8px 16px;
|
|
padding-top: 8px;
|
|
padding-bottom: 8px;
|
|
}
|
|
.desktop-banner-title {
|
|
margin: 0;
|
|
font-weight: 700;
|
|
}
|
|
.desktop-banner-copy {
|
|
margin: 4px 0 0 0;
|
|
}
|
|
.desktop-banner .uk-button {
|
|
white-space: nowrap;
|
|
}
|
|
.uk-light .desktop-banner {
|
|
border-color: rgba(204, 151, 46, 0.7);
|
|
background: rgba(204, 151, 46, 0.16);
|
|
}
|
|
@media only screen and (max-width: 640px) {
|
|
.desktop-banner {
|
|
text-align: center;
|
|
}
|
|
.desktop-banner .uk-button {
|
|
width: 100%;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body class="uk-dark">
|
|
<input type="file" id="imageInput" accept="image/*" style="display: none;">
|
|
<div class="container uk-padding" uk-grid>
|
|
<div class="uk-width-1-1 uk-navbar-container uk-navbar-transparent" uk-navbar>
|
|
<div class="uk-navbar-left uk-text-large uk-text-bolder">
|
|
<a id="vc-logo" class="uk-logo" href="//www.visioncortex.org"><img style="height: 50px; margin: -30px 0 -25px 0;" src="./assets/visioncortex-logo.svg"></a>
|
|
<a id="vc-logo-light" class="uk-logo" href="//www.visioncortex.org"><img style="height: 50px; margin: -30px 0 -25px 0;" src="./assets/visioncortex-logo-light.svg"></a>
|
|
|
|
/ VTracer
|
|
</div>
|
|
<div class="uk-navbar-right uk-padding-small">
|
|
<a class="uk-button uk-button-default" href="//github.com/visioncortex/vtracer">GitHub</a>
|
|
|
|
<a id="export" class="uk-button uk-button-primary">Download as SVG</a>
|
|
</div>
|
|
</div>
|
|
<div class="uk-width-1-1">
|
|
<div class="desktop-banner uk-padding-small uk-flex uk-flex-middle uk-flex-between uk-flex-wrap">
|
|
<div class="uk-margin-small-right">
|
|
<p id="desktop-banner-copy" class="desktop-banner-title">Try the latest VTracer Desktop App for native speed, curve simplification, and higher tracing quality.</p>
|
|
</div>
|
|
<a id="desktop-download" class="uk-button uk-button-primary" href="https://github.com/visioncortex/vtracer/releases/latest" rel="noopener">
|
|
Download Desktop App
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<div id="progressregion" class="uk-width-1-1 uk-padding-small" style="display: none;">
|
|
<progress id="progressbar" class="uk-progress uk-align-right" value="0" max="100" style="width: 98%;"></progress>
|
|
</div>
|
|
<div class="uk-width-3-4">
|
|
<div id="drop" class="uk-padding uk-flex uk-flex-center">
|
|
<div id="canvas-container" class="uk-width-1-1">
|
|
<div id="droptext" class="uk-flex uk-flex-middle uk-flex-center">
|
|
<p>Drag an image here, Cmd-V to paste or <a href="#" id="imageSelect">Select file</a></p>
|
|
</div>
|
|
<canvas id="frame"></canvas>
|
|
<svg id="svg" version="1.1" xmlns="http://www.w3.org/2000/svg"></svg>
|
|
</div>
|
|
</div>
|
|
<div id="gallery" class="uk-padding">
|
|
<div class="uk-inline" uk-slider>
|
|
<ul id="galleryslider" class="uk-slider-items uk-child-width-1-6 uk-grid uk-grid-small">
|
|
|
|
</ul>
|
|
<a class="uk-position-center-left uk-position-small uk-hidden-hover" href="#" uk-slidenav-previous uk-slider-item="previous"></a>
|
|
<a class="uk-position-center-right uk-position-small uk-hidden-hover" href="#" uk-slidenav-next uk-slider-item="next"></a>
|
|
</div>
|
|
<a class="uk-align-right uk-text-meta" style="margin:-5px;" uk-toggle="target: #credits-modal">Photo Credits</a>
|
|
</div>
|
|
</div>
|
|
<div id="options" class="uk-width-1-4 uk-height-1-1 uk-grid-small uk-flex uk-flex-center" uk-grid>
|
|
|
|
<div class="uk-width-1-1 uk-flex uk-flex-right">
|
|
|
|
</div>
|
|
|
|
<p></p>
|
|
|
|
<div class="uk-width-1-1 uk-flex uk-flex-right">
|
|
<div class="uk-text-bold" uk-tooltip="pos:left; title: Algorithm for segmentation and grouping of pixel clusters">
|
|
Clustering
|
|
</div>
|
|
</div>
|
|
|
|
<div class="uk-width-1-1 uk-flex uk-flex-right">
|
|
<div class="uk-button-group">
|
|
<button id="clustering-binary" class="uk-button uk-button-default" uk-tooltip="Black & White (Binary Image)">B/W</button>
|
|
<button id="clustering-color" class="selected uk-button uk-button-default" uk-tooltip="True Color Image">Color</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="clustering-color-options uk-width-1-1 uk-flex uk-flex-right">
|
|
<div class="uk-button-group">
|
|
<button id="clustering-cutout" class="uk-button uk-button-default uk-button-small" uk-tooltip="Shapes disjoint with others">Cutout</button>
|
|
<button id="clustering-stacked" class="selected uk-button uk-button-default uk-button-small" uk-tooltip="Stack shapes on top of another">Stacked</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="uk-width-1-1 uk-flex uk-flex-right">
|
|
<div uk-tooltip="pos: left; title: Discard patches small than X px in size">
|
|
Filter Speckle <span class="uk-text-meta">(Cleaner)</span>
|
|
</div>
|
|
</div>
|
|
<div id="filterspecklevalue" class="uk-width-1-6">
|
|
4
|
|
</div>
|
|
<div class="uk-width-5-6">
|
|
<input id="filterspeckle" class="uk-range" type="range" min="0" max="128" step="1" value="4">
|
|
</div>
|
|
|
|
<div class="clustering-color-options uk-width-1-1 uk-flex uk-flex-right">
|
|
<div uk-tooltip="pos: left; title: Number of significant bits to use in a RGB channel">
|
|
Color Precision <span class="uk-text-meta">(More accurate)</span>
|
|
</div>
|
|
</div>
|
|
<div id="colorprecisionvalue" class="clustering-color-options uk-width-1-6">
|
|
6
|
|
</div>
|
|
<div class="clustering-color-options uk-width-5-6">
|
|
<input id="colorprecision" class="uk-range" type="range" min="1" max="8" step="1" value="6">
|
|
</div>
|
|
|
|
<div class="clustering-color-options uk-width-1-1 uk-flex uk-flex-right">
|
|
<div uk-tooltip="pos: left; title: Color difference between gradient layers">
|
|
Gradient Step <span class="uk-text-meta">(Less layers)</span>
|
|
</div>
|
|
</div>
|
|
<div id="layerdifferencevalue" class="clustering-color-options uk-width-1-6">
|
|
16
|
|
</div>
|
|
<div class="clustering-color-options uk-width-5-6">
|
|
<input id="layerdifference" class="uk-range" type="range" min="0" max="128" step="1" value="16">
|
|
</div>
|
|
|
|
<div class="uk-width-1-1 uk-flex uk-flex-right">
|
|
<div class="uk-text-bold" uk-tooltip="pos: left; title: Algorithm for converting clusters to shapes">
|
|
Curve Fitting
|
|
</div>
|
|
</div>
|
|
|
|
<div class="uk-width-1-1 uk-flex uk-flex-right">
|
|
<div class="uk-button-group">
|
|
<button id="none" class="uk-button uk-button-default" uk-tooltip="Exact cluster boundary">PIXEL</button>
|
|
<button id="polygon" class="uk-button uk-button-default" uk-tooltip="Simplify to Polygon">Polygon</button>
|
|
<button id="spline" class="selected uk-button uk-button-default" uk-tooltip="Smooth and Curve-fit">Spline</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="spline-options uk-width-1-1 uk-flex uk-flex-right">
|
|
<div uk-tooltip="pos: left; title: Minimum Momentary Angle (in degrees) to be considered a corner (to be kept after smoothing)">
|
|
Corner Threshold <span class="uk-text-meta">(Smoother)</span>
|
|
</div>
|
|
</div>
|
|
<div id="cornervalue" class="spline-options uk-width-1-6">
|
|
60
|
|
</div>
|
|
<div class="spline-options uk-width-5-6">
|
|
<input id="corner" class="uk-range" type="range" min="0" max="180" step="1" value="60">
|
|
</div>
|
|
|
|
<div class="spline-options uk-width-1-1 uk-flex uk-flex-right">
|
|
<div uk-tooltip="pos: left; title: Perform Iterative Subdivide Smooth until all segments are shorter than this length">
|
|
Segment Length <span class="uk-text-meta">(More coarse)</span>
|
|
</div>
|
|
</div>
|
|
<div id="lengthvalue" class="spline-options uk-width-1-6">
|
|
4
|
|
</div>
|
|
<div class="spline-options uk-width-5-6">
|
|
<input id="length" class="uk-range" type="range" min="3.5" max="10" step="0.5" value="4">
|
|
</div>
|
|
|
|
<div class="spline-options uk-width-1-1 uk-flex uk-flex-right">
|
|
<div uk-tooltip="pos: left; title: Minimum Angle Displacement (in degrees) to be considered a cutting point between curves">
|
|
Splice Threshold <span class="uk-text-meta">(Less accurate)</span>
|
|
</div>
|
|
</div>
|
|
<div id="splicevalue" class="spline-options uk-width-1-6">
|
|
45
|
|
</div>
|
|
<div class="spline-options uk-width-5-6">
|
|
<input id="splice" class="uk-range" type="range" min="0" max="180" step="1" value="45">
|
|
</div>
|
|
|
|
<div style="display: none">
|
|
<div class="spline-options uk-width-1-1 uk-flex uk-flex-right">
|
|
<div uk-tooltip="pos: left; title: Number of decimal places used in svg path string">
|
|
Path Precision <span class="uk-text-meta">(More digits)</span>
|
|
</div>
|
|
</div>
|
|
<div id="pathprecisionvalue" class="spline-options uk-width-1-6">
|
|
8
|
|
</div>
|
|
<div class="spline-options uk-width-5-6">
|
|
<input id="pathprecision" class="uk-range" type="range" min="0" max="16" step="1" value="8">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="credits-modal" uk-modal>
|
|
<div id="credits-modal-content" class="uk-modal-dialog uk-modal-body">
|
|
<p>Image Credits</p>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
|
document.body.classList.remove('uk-dark');
|
|
document.body.classList.add('uk-light');
|
|
}
|
|
|
|
(function() {
|
|
var releasesUrl = 'https://github.com/visioncortex/vtracer/releases/latest';
|
|
var manifestUrl = 'https://raw.githubusercontent.com/visioncortex/vtracer/desktop-updates/latest.json';
|
|
var downloadLink = document.getElementById('desktop-download');
|
|
var bannerCopy = document.getElementById('desktop-banner-copy');
|
|
|
|
function detectPlatform() {
|
|
var platform = navigator.userAgentData && navigator.userAgentData.platform
|
|
? navigator.userAgentData.platform
|
|
: navigator.platform || '';
|
|
var userAgent = navigator.userAgent || '';
|
|
var platformText = platform + ' ' + userAgent;
|
|
|
|
if (/Windows/i.test(platformText)) {
|
|
return {
|
|
label: 'Windows',
|
|
manifestKeys: ['windows-x86_64']
|
|
};
|
|
}
|
|
|
|
if (/Mac|Darwin/i.test(platformText) && !/iPhone|iPad|iPod/i.test(userAgent)) {
|
|
return {
|
|
label: 'macOS',
|
|
manifestKeys: ['macos-universal', 'darwin-universal', 'darwin-aarch64', 'darwin-x86_64']
|
|
};
|
|
}
|
|
|
|
if (/Linux|X11/i.test(platformText) && !/Android/i.test(userAgent)) {
|
|
return {
|
|
label: 'Linux',
|
|
manifestKeys: ['linux-x86_64']
|
|
};
|
|
}
|
|
|
|
return {
|
|
label: '',
|
|
manifestKeys: []
|
|
};
|
|
}
|
|
|
|
function setFallback(platform) {
|
|
downloadLink.href = releasesUrl;
|
|
downloadLink.textContent = platform.label ? 'Download for ' + platform.label : 'View latest downloads';
|
|
bannerCopy.textContent = platform.label
|
|
? 'Try the latest VTracer Desktop App for ' + platform.label + ' with native speed, curve simplification, and higher tracing quality.'
|
|
: 'Try the latest VTracer Desktop App for native speed, curve simplification, and higher tracing quality.';
|
|
}
|
|
|
|
function findPlatformDownload(platforms, platform) {
|
|
var keys = platform.manifestKeys || [];
|
|
|
|
for (var i = 0; i < keys.length; i++) {
|
|
if (platforms[keys[i]] && platforms[keys[i]].url) {
|
|
return platforms[keys[i]].url;
|
|
}
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
function publicDownloadUrl(platform, updaterUrl) {
|
|
if (platform.label !== 'macOS' || !updaterUrl) {
|
|
return updaterUrl;
|
|
}
|
|
|
|
// The manifest points at Tauri's updater bundle; a person wants the
|
|
// disk image sitting beside it. Rewrite only the filename, so the
|
|
// release tag comes from the URL we were handed: manifest.version
|
|
// carries a build suffix the tag does not, and building the URL from
|
|
// it 404s (version '1.0.0-alpha.3.app.59' lives under tag
|
|
// '1.0.0-alpha.3').
|
|
return updaterUrl.replace(/\.app\.tar\.gz$/i, '.dmg');
|
|
}
|
|
|
|
var platform = detectPlatform();
|
|
setFallback(platform);
|
|
|
|
downloadLink.addEventListener('click', function() {
|
|
if (typeof gtag !== 'function') {
|
|
return;
|
|
}
|
|
|
|
gtag('event', 'desktop_download_click', {
|
|
platform: platform.label || 'unknown',
|
|
source: 'docs_banner',
|
|
download_url: downloadLink.href,
|
|
transport_type: 'beacon'
|
|
});
|
|
});
|
|
|
|
if (!window.fetch) {
|
|
return;
|
|
}
|
|
|
|
fetch(manifestUrl)
|
|
.then(function(response) {
|
|
if (!response.ok) {
|
|
throw new Error('Desktop manifest lookup failed');
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(function(manifest) {
|
|
var updaterUrl = findPlatformDownload(manifest.platforms || {}, platform);
|
|
var downloadUrl = publicDownloadUrl(platform, updaterUrl);
|
|
|
|
if (!downloadUrl) {
|
|
return;
|
|
}
|
|
|
|
downloadLink.href = downloadUrl;
|
|
downloadLink.textContent = 'Download for ' + platform.label;
|
|
|
|
bannerCopy.textContent = 'Try the latest VTracer Desktop App for ' + platform.label + ' with native speed, curve simplification, and higher tracing quality.';
|
|
})
|
|
.catch(function() {
|
|
setFallback(platform);
|
|
});
|
|
})();
|
|
</script>
|
|
<script src="./bootstrap.js"></script>
|
|
</body>
|
|
</html>
|