From 57d8cfde7a275dddd9f5a417b6ad7ae680fe1f7f Mon Sep 17 00:00:00 2001 From: FzzySwtr Date: Mon, 15 Jun 2026 19:00:42 -0700 Subject: [PATCH 01/13] Add `ollama serve` hint, breathing status dot, smaller hero title - Hero title reduced ~30% (2.4rem -> 1.68rem; mobile 1.7rem -> 1.19rem) - Amber "Ollama unavailable" status dot now pulses with a slow, calm breathing glow; respects prefers-reduced-motion - Lede and the catalog "unavailable" note now tell users to run `ollama serve` in a terminal application; the command renders as a click-to-copy command chip Co-Authored-By: Claude Opus 4.8 (1M context) --- index.html | 46 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/index.html b/index.html index 47ead70..c5b7435 100644 --- a/index.html +++ b/index.html @@ -165,17 +165,26 @@ } .pill .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--muted-2); } .pill .dot.ok { background: var(--success); } - .pill .dot.warn { background: #c98a00; } + /* Amber "Ollama unavailable" indicator — slow, calm breathing glow. */ + .pill .dot.warn { + background: #c98a00; + animation: ollama-breathe 3.4s ease-in-out infinite; + } + @keyframes ollama-breathe { + 0%, 100% { box-shadow: 0 0 0 0 rgba(201,138,0,0); opacity: .7; transform: scale(1); } + 50% { box-shadow: 0 0 9px 2px rgba(201,138,0,.7); opacity: 1; transform: scale(1.15); } + } + @media (prefers-reduced-motion: reduce) { .pill .dot.warn { animation: none; } } @media (max-width: 640px) { .topbar-meta .pill.hide-sm { display: none; } } /* ---- Hero ---- */ .hero { padding: 46px 0 8px; } .hero h1 { - font-size: 2.4rem; font-weight: 800; letter-spacing: -.025em; + font-size: 1.68rem; font-weight: 800; letter-spacing: -.025em; line-height: 1.08; max-width: 24ch; } .hero p.lede { margin-top: 14px; color: var(--muted); font-size: 1.08rem; max-width: 62ch; } - @media (max-width: 768px) { .hero { padding: 30px 0 4px; } .hero h1 { font-size: 1.7rem; } } + @media (max-width: 768px) { .hero { padding: 30px 0 4px; } .hero h1 { font-size: 1.19rem; } } .summary { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin-top: 28px; @@ -213,6 +222,18 @@ .note.brand strong, .note.warn strong, .note.error strong { color: #fff; } .note.brand a { color: #fff; text-decoration: underline; } + /* Inline command chip — click to copy (e.g. `ollama serve`). */ + code.cmd { + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + font-size: .9em; background: var(--surface-2); border: 1px solid var(--border); + border-radius: 6px; padding: 1px 7px; color: var(--ink); + cursor: pointer; white-space: nowrap; transition: background .12s ease; + } + code.cmd:hover { background: var(--surface); } + code.cmd.copied { color: var(--success); border-color: var(--success); } + .note.warn code.cmd { background: rgba(255,255,255,.18); border-color: rgba(255,255,255,.4); color: #fff; } + .note.warn code.cmd.copied { color: #fff; border-color: #fff; } + /* ---- Tags / chips ---- */ .tags { display: flex; flex-wrap: wrap; gap: 6px; } .tag { @@ -393,7 +414,7 @@

Local LLM benchmark
& intelligence catalog

-

Measure throughput for the models you run locally via Ollama, and compare their skills at various tasks.

+

Measure throughput for the models you run locally via Ollama, and compare their skills at various tasks. Run Ollama with ollama serve in a terminal application.

Catalog models
Curated Companion catalog
@@ -590,13 +611,28 @@

${escapeHtml(prompt.name)}

else { dot.className = 'dot'; label.textContent = 'Connecting…'; } } + // Click an inline command chip (e.g. `ollama serve`) to copy it to the clipboard. + document.addEventListener('click', (event) => { + const chip = event.target.closest('code.cmd'); + if (!chip || chip.classList.contains('copied')) return; + const command = chip.textContent; + const showCopied = () => { + chip.classList.add('copied'); + chip.textContent = 'Copied!'; + setTimeout(() => { chip.textContent = command; chip.classList.remove('copied'); }, 1100); + }; + if (navigator.clipboard && navigator.clipboard.writeText) { + navigator.clipboard.writeText(command).then(showCopied).catch(() => {}); + } + }); + function updateModelCatalogSummary(models, options = {}) { const summary = document.getElementById('modelCatalogSummary'); const installedCount = models.filter(m => m.installed).length; const supportedCount = models.filter(m => m.supported).length; const customInstalledCount = models.filter(m => m.installed && !m.supported).length; const note = options.ollamaUnavailable - ? 'Ollama is currently unavailable — showing the curated catalog only. Start Ollama to enable benchmarking.' + ? 'Ollama is currently unavailable — showing the curated catalog only. Open a terminal application and run ollama serve to start it, then refresh to enable benchmarking.' : 'Disabled cards are in the curated catalog but not installed locally.'; summary.innerHTML = ` ${installedCount} installed From 3122bab55bffa4338b153d90b375987db94fc69a Mon Sep 17 00:00:00 2001 From: FzzySwtr Date: Mon, 15 Jun 2026 19:05:21 -0700 Subject: [PATCH 02/13] Increase brand logo height to 40px Co-Authored-By: Claude Opus 4.8 (1M context) --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index c5b7435..7ae7cd8 100644 --- a/index.html +++ b/index.html @@ -131,7 +131,7 @@ gap: 16px; padding: 14px 0; } .brand { display: flex; align-items: center; gap: 12px; } - .brand-logo { height: 30px; width: auto; flex: 0 0 auto; display: block; } + .brand-logo { height: 40px; width: auto; flex: 0 0 auto; display: block; } /* Centered wordmark — all caps, 400 weight, cap-height matched to the logo icon's outer coloured edge (~28.7px → ~41px Montserrat). */ .brand-app { From fa8ca8152a6b33087ab5abca45160ee3e7104cc2 Mon Sep 17 00:00:00 2001 From: FzzySwtr Date: Mon, 15 Jun 2026 19:18:01 -0700 Subject: [PATCH 03/13] Add branded PDF export of benchmark results; shrink wordmark to 34px - New "Export PDF" button in the Detailed results header, hidden until a benchmark has run (results present), then shown - exportResultsPdf() builds a branded PDF via jsPDF: a teal LOCAL-BENCH / Companion Intelligence header band, system summary, summary stat boxes, top-performer line, and a paginated results table (Model, IQ, Tokens/s, Total tokens, Duration, Timestamp, colored Status) with a confidential footer and page numbers - Vendor jsPDF 2.5.2 UMD locally in assets/ (same pattern as Chart.js) - Reduce top-bar wordmark (.brand-app) from 41px to 34px Co-Authored-By: Claude Opus 4.8 (1M context) --- assets/jspdf.umd.min.js | 398 ++++++++++++++++++++++++++++++++++++++++ index.html | 167 ++++++++++++++++- 2 files changed, 563 insertions(+), 2 deletions(-) create mode 100644 assets/jspdf.umd.min.js diff --git a/assets/jspdf.umd.min.js b/assets/jspdf.umd.min.js new file mode 100644 index 0000000..5793b5a --- /dev/null +++ b/assets/jspdf.umd.min.js @@ -0,0 +1,398 @@ +/** @license + * + * jsPDF - PDF Document creation from JavaScript + * Version 2.5.2 Built on 2024-09-17T13:29:57.856Z + * CommitID 00000000 + * + * Copyright (c) 2010-2021 James Hall , https://github.com/MrRio/jsPDF + * 2015-2021 yWorks GmbH, http://www.yworks.com + * 2015-2021 Lukas Holländer , https://github.com/HackbrettXXX + * 2016-2018 Aras Abbasi + * 2010 Aaron Spike, https://github.com/acspike + * 2012 Willow Systems Corporation, https://github.com/willowsystems + * 2012 Pablo Hess, https://github.com/pablohess + * 2012 Florian Jenett, https://github.com/fjenett + * 2013 Warren Weckesser, https://github.com/warrenweckesser + * 2013 Youssef Beddad, https://github.com/lifof + * 2013 Lee Driscoll, https://github.com/lsdriscoll + * 2013 Stefan Slonevskiy, https://github.com/stefslon + * 2013 Jeremy Morel, https://github.com/jmorel + * 2013 Christoph Hartmann, https://github.com/chris-rock + * 2014 Juan Pablo Gaviria, https://github.com/juanpgaviria + * 2014 James Makes, https://github.com/dollaruw + * 2014 Diego Casorran, https://github.com/diegocr + * 2014 Steven Spungin, https://github.com/Flamenco + * 2014 Kenneth Glassey, https://github.com/Gavvers + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Contributor(s): + * siefkenj, ahwolf, rickygu, Midnith, saintclair, eaparango, + * kim3er, mfo, alnorth, Flamenco + */ + +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t=t||self).jspdf={})}(this,(function(t){"use strict";function e(t){return(e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}var r=function(){return"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this}();function n(){r.console&&"function"==typeof r.console.log&&r.console.log.apply(r.console,arguments)}var i={log:n,warn:function(t){r.console&&("function"==typeof r.console.warn?r.console.warn.apply(r.console,arguments):n.call(null,arguments))},error:function(t){r.console&&("function"==typeof r.console.error?r.console.error.apply(r.console,arguments):n(t))}};function a(t,e,r){var n=new XMLHttpRequest;n.open("GET",t),n.responseType="blob",n.onload=function(){l(n.response,e,r)},n.onerror=function(){i.error("could not download file")},n.send()}function o(t){var e=new XMLHttpRequest;e.open("HEAD",t,!1);try{e.send()}catch(t){}return e.status>=200&&e.status<=299}function s(t){try{t.dispatchEvent(new MouseEvent("click"))}catch(r){var e=document.createEvent("MouseEvents");e.initMouseEvent("click",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),t.dispatchEvent(e)}}var c,u,l=r.saveAs||("object"!==("undefined"==typeof window?"undefined":e(window))||window!==r?function(){}:"undefined"!=typeof HTMLAnchorElement&&"download"in HTMLAnchorElement.prototype?function(t,e,n){var i=r.URL||r.webkitURL,c=document.createElement("a");e=e||t.name||"download",c.download=e,c.rel="noopener","string"==typeof t?(c.href=t,c.origin!==location.origin?o(c.href)?a(t,e,n):s(c,c.target="_blank"):s(c)):(c.href=i.createObjectURL(t),setTimeout((function(){i.revokeObjectURL(c.href)}),4e4),setTimeout((function(){s(c)}),0))}:"msSaveOrOpenBlob"in navigator?function(t,r,n){if(r=r||t.name||"download","string"==typeof t)if(o(t))a(t,r,n);else{var c=document.createElement("a");c.href=t,c.target="_blank",setTimeout((function(){s(c)}))}else navigator.msSaveOrOpenBlob(function(t,r){return void 0===r?r={autoBom:!1}:"object"!==e(r)&&(i.warn("Deprecated: Expected third argument to be a object"),r={autoBom:!r}),r.autoBom&&/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(t.type)?new Blob([String.fromCharCode(65279),t],{type:t.type}):t}(t,n),r)}:function(t,n,i,o){if((o=o||open("","_blank"))&&(o.document.title=o.document.body.innerText="downloading..."),"string"==typeof t)return a(t,n,i);var s="application/octet-stream"===t.type,c=/constructor/i.test(r.HTMLElement)||r.safari,u=/CriOS\/[\d]+/.test(navigator.userAgent);if((u||s&&c)&&"object"===("undefined"==typeof FileReader?"undefined":e(FileReader))){var l=new FileReader;l.onloadend=function(){var t=l.result;t=u?t:t.replace(/^data:[^;]*;/,"data:attachment/file;"),o?o.location.href=t:location=t,o=null},l.readAsDataURL(t)}else{var h=r.URL||r.webkitURL,f=h.createObjectURL(t);o?o.location=f:location.href=f,o=null,setTimeout((function(){h.revokeObjectURL(f)}),4e4)}}); +/** + * A class to parse color values + * @author Stoyan Stefanov + * {@link http://www.phpied.com/rgb-color-parser-in-javascript/} + * @license Use it if you like it + */function h(t){var e;t=t||"",this.ok=!1,"#"==t.charAt(0)&&(t=t.substr(1,6));t={aliceblue:"f0f8ff",antiquewhite:"faebd7",aqua:"00ffff",aquamarine:"7fffd4",azure:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"000000",blanchedalmond:"ffebcd",blue:"0000ff",blueviolet:"8a2be2",brown:"a52a2a",burlywood:"deb887",cadetblue:"5f9ea0",chartreuse:"7fff00",chocolate:"d2691e",coral:"ff7f50",cornflowerblue:"6495ed",cornsilk:"fff8dc",crimson:"dc143c",cyan:"00ffff",darkblue:"00008b",darkcyan:"008b8b",darkgoldenrod:"b8860b",darkgray:"a9a9a9",darkgreen:"006400",darkkhaki:"bdb76b",darkmagenta:"8b008b",darkolivegreen:"556b2f",darkorange:"ff8c00",darkorchid:"9932cc",darkred:"8b0000",darksalmon:"e9967a",darkseagreen:"8fbc8f",darkslateblue:"483d8b",darkslategray:"2f4f4f",darkturquoise:"00ced1",darkviolet:"9400d3",deeppink:"ff1493",deepskyblue:"00bfff",dimgray:"696969",dodgerblue:"1e90ff",feldspar:"d19275",firebrick:"b22222",floralwhite:"fffaf0",forestgreen:"228b22",fuchsia:"ff00ff",gainsboro:"dcdcdc",ghostwhite:"f8f8ff",gold:"ffd700",goldenrod:"daa520",gray:"808080",green:"008000",greenyellow:"adff2f",honeydew:"f0fff0",hotpink:"ff69b4",indianred:"cd5c5c",indigo:"4b0082",ivory:"fffff0",khaki:"f0e68c",lavender:"e6e6fa",lavenderblush:"fff0f5",lawngreen:"7cfc00",lemonchiffon:"fffacd",lightblue:"add8e6",lightcoral:"f08080",lightcyan:"e0ffff",lightgoldenrodyellow:"fafad2",lightgrey:"d3d3d3",lightgreen:"90ee90",lightpink:"ffb6c1",lightsalmon:"ffa07a",lightseagreen:"20b2aa",lightskyblue:"87cefa",lightslateblue:"8470ff",lightslategray:"778899",lightsteelblue:"b0c4de",lightyellow:"ffffe0",lime:"00ff00",limegreen:"32cd32",linen:"faf0e6",magenta:"ff00ff",maroon:"800000",mediumaquamarine:"66cdaa",mediumblue:"0000cd",mediumorchid:"ba55d3",mediumpurple:"9370d8",mediumseagreen:"3cb371",mediumslateblue:"7b68ee",mediumspringgreen:"00fa9a",mediumturquoise:"48d1cc",mediumvioletred:"c71585",midnightblue:"191970",mintcream:"f5fffa",mistyrose:"ffe4e1",moccasin:"ffe4b5",navajowhite:"ffdead",navy:"000080",oldlace:"fdf5e6",olive:"808000",olivedrab:"6b8e23",orange:"ffa500",orangered:"ff4500",orchid:"da70d6",palegoldenrod:"eee8aa",palegreen:"98fb98",paleturquoise:"afeeee",palevioletred:"d87093",papayawhip:"ffefd5",peachpuff:"ffdab9",peru:"cd853f",pink:"ffc0cb",plum:"dda0dd",powderblue:"b0e0e6",purple:"800080",red:"ff0000",rosybrown:"bc8f8f",royalblue:"4169e1",saddlebrown:"8b4513",salmon:"fa8072",sandybrown:"f4a460",seagreen:"2e8b57",seashell:"fff5ee",sienna:"a0522d",silver:"c0c0c0",skyblue:"87ceeb",slateblue:"6a5acd",slategray:"708090",snow:"fffafa",springgreen:"00ff7f",steelblue:"4682b4",tan:"d2b48c",teal:"008080",thistle:"d8bfd8",tomato:"ff6347",turquoise:"40e0d0",violet:"ee82ee",violetred:"d02090",wheat:"f5deb3",white:"ffffff",whitesmoke:"f5f5f5",yellow:"ffff00",yellowgreen:"9acd32"}[t=(t=t.replace(/ /g,"")).toLowerCase()]||t;for(var r=[{re:/^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/,example:["rgb(123, 234, 45)","rgb(255,234,245)"],process:function(t){return[parseInt(t[1]),parseInt(t[2]),parseInt(t[3])]}},{re:/^(\w{2})(\w{2})(\w{2})$/,example:["#00ff00","336699"],process:function(t){return[parseInt(t[1],16),parseInt(t[2],16),parseInt(t[3],16)]}},{re:/^(\w{1})(\w{1})(\w{1})$/,example:["#fb0","f0f"],process:function(t){return[parseInt(t[1]+t[1],16),parseInt(t[2]+t[2],16),parseInt(t[3]+t[3],16)]}}],n=0;n255?255:this.r,this.g=this.g<0||isNaN(this.g)?0:this.g>255?255:this.g,this.b=this.b<0||isNaN(this.b)?0:this.b>255?255:this.b,this.toRGB=function(){return"rgb("+this.r+", "+this.g+", "+this.b+")"},this.toHex=function(){var t=this.r.toString(16),e=this.g.toString(16),r=this.b.toString(16);return 1==t.length&&(t="0"+t),1==e.length&&(e="0"+e),1==r.length&&(r="0"+r),"#"+t+e+r}} +/** + * @license + * Joseph Myers does not specify a particular license for his work. + * + * Author: Joseph Myers + * Accessed from: http://www.myersdaily.org/joseph/javascript/md5.js + * + * Modified by: Owen Leong + */ +function f(t,e){var r=t[0],n=t[1],i=t[2],a=t[3];r=p(r,n,i,a,e[0],7,-680876936),a=p(a,r,n,i,e[1],12,-389564586),i=p(i,a,r,n,e[2],17,606105819),n=p(n,i,a,r,e[3],22,-1044525330),r=p(r,n,i,a,e[4],7,-176418897),a=p(a,r,n,i,e[5],12,1200080426),i=p(i,a,r,n,e[6],17,-1473231341),n=p(n,i,a,r,e[7],22,-45705983),r=p(r,n,i,a,e[8],7,1770035416),a=p(a,r,n,i,e[9],12,-1958414417),i=p(i,a,r,n,e[10],17,-42063),n=p(n,i,a,r,e[11],22,-1990404162),r=p(r,n,i,a,e[12],7,1804603682),a=p(a,r,n,i,e[13],12,-40341101),i=p(i,a,r,n,e[14],17,-1502002290),r=g(r,n=p(n,i,a,r,e[15],22,1236535329),i,a,e[1],5,-165796510),a=g(a,r,n,i,e[6],9,-1069501632),i=g(i,a,r,n,e[11],14,643717713),n=g(n,i,a,r,e[0],20,-373897302),r=g(r,n,i,a,e[5],5,-701558691),a=g(a,r,n,i,e[10],9,38016083),i=g(i,a,r,n,e[15],14,-660478335),n=g(n,i,a,r,e[4],20,-405537848),r=g(r,n,i,a,e[9],5,568446438),a=g(a,r,n,i,e[14],9,-1019803690),i=g(i,a,r,n,e[3],14,-187363961),n=g(n,i,a,r,e[8],20,1163531501),r=g(r,n,i,a,e[13],5,-1444681467),a=g(a,r,n,i,e[2],9,-51403784),i=g(i,a,r,n,e[7],14,1735328473),r=m(r,n=g(n,i,a,r,e[12],20,-1926607734),i,a,e[5],4,-378558),a=m(a,r,n,i,e[8],11,-2022574463),i=m(i,a,r,n,e[11],16,1839030562),n=m(n,i,a,r,e[14],23,-35309556),r=m(r,n,i,a,e[1],4,-1530992060),a=m(a,r,n,i,e[4],11,1272893353),i=m(i,a,r,n,e[7],16,-155497632),n=m(n,i,a,r,e[10],23,-1094730640),r=m(r,n,i,a,e[13],4,681279174),a=m(a,r,n,i,e[0],11,-358537222),i=m(i,a,r,n,e[3],16,-722521979),n=m(n,i,a,r,e[6],23,76029189),r=m(r,n,i,a,e[9],4,-640364487),a=m(a,r,n,i,e[12],11,-421815835),i=m(i,a,r,n,e[15],16,530742520),r=v(r,n=m(n,i,a,r,e[2],23,-995338651),i,a,e[0],6,-198630844),a=v(a,r,n,i,e[7],10,1126891415),i=v(i,a,r,n,e[14],15,-1416354905),n=v(n,i,a,r,e[5],21,-57434055),r=v(r,n,i,a,e[12],6,1700485571),a=v(a,r,n,i,e[3],10,-1894986606),i=v(i,a,r,n,e[10],15,-1051523),n=v(n,i,a,r,e[1],21,-2054922799),r=v(r,n,i,a,e[8],6,1873313359),a=v(a,r,n,i,e[15],10,-30611744),i=v(i,a,r,n,e[6],15,-1560198380),n=v(n,i,a,r,e[13],21,1309151649),r=v(r,n,i,a,e[4],6,-145523070),a=v(a,r,n,i,e[11],10,-1120210379),i=v(i,a,r,n,e[2],15,718787259),n=v(n,i,a,r,e[9],21,-343485551),t[0]=S(r,t[0]),t[1]=S(n,t[1]),t[2]=S(i,t[2]),t[3]=S(a,t[3])}function d(t,e,r,n,i,a){return e=S(S(e,t),S(n,a)),S(e<>>32-i,r)}function p(t,e,r,n,i,a,o){return d(e&r|~e&n,t,e,i,a,o)}function g(t,e,r,n,i,a,o){return d(e&n|r&~n,t,e,i,a,o)}function m(t,e,r,n,i,a,o){return d(e^r^n,t,e,i,a,o)}function v(t,e,r,n,i,a,o){return d(r^(e|~n),t,e,i,a,o)}function b(t){var e,r=t.length,n=[1732584193,-271733879,-1732584194,271733878];for(e=64;e<=t.length;e+=64)f(n,y(t.substring(e-64,e)));t=t.substring(e-64);var i=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];for(e=0;e>2]|=t.charCodeAt(e)<<(e%4<<3);if(i[e>>2]|=128<<(e%4<<3),e>55)for(f(n,i),e=0;e<16;e++)i[e]=0;return i[14]=8*r,f(n,i),n}function y(t){var e,r=[];for(e=0;e<64;e+=4)r[e>>2]=t.charCodeAt(e)+(t.charCodeAt(e+1)<<8)+(t.charCodeAt(e+2)<<16)+(t.charCodeAt(e+3)<<24);return r}c=r.atob.bind(r),u=r.btoa.bind(r);var w="0123456789abcdef".split("");function N(t){for(var e="",r=0;r<4;r++)e+=w[t>>8*r+4&15]+w[t>>8*r&15];return e}function L(t){return String.fromCharCode((255&t)>>0,(65280&t)>>8,(16711680&t)>>16,(4278190080&t)>>24)}function A(t){return function(t){return t.map(L).join("")}(b(t))}var x="5d41402abc4b2a76b9719d911017c592"!=function(t){for(var e=0;e>16)+(e>>16)+(r>>16)<<16|65535&r}return t+e&4294967295} +/** + * @license + * FPDF is released under a permissive license: there is no usage restriction. + * You may embed it freely in your application (commercial or not), with or + * without modifications. + * + * Reference: http://www.fpdf.org/en/script/script37.php + */function _(t,e){var r,n,i,a;if(t!==r){for(var o=(i=t,a=1+(256/t.length>>0),new Array(a+1).join(i)),s=[],c=0;c<256;c++)s[c]=c;var u=0;for(c=0;c<256;c++){var l=s[c];u=(u+l+o.charCodeAt(c))%256,s[c]=s[u],s[u]=l}r=t,n=s}else s=n;var h=e.length,f=0,d=0,p="";for(c=0;c€/\f©þdSiz";var a=(e+this.padding).substr(0,32),o=(r+this.padding).substr(0,32);this.O=this.processOwnerPassword(a,o),this.P=-(1+(255^i)),this.encryptionKey=A(a+this.O+this.lsbFirstWord(this.P)+this.hexToBytes(n)).substr(0,5),this.U=_(this.encryptionKey,this.padding)}function F(t){if(/[^\u0000-\u00ff]/.test(t))throw new Error("Invalid PDF Name Object: "+t+", Only accept ASCII characters.");for(var e="",r=t.length,n=0;n126)e+="#"+("0"+i.toString(16)).slice(-2);else e+=t[n]}return e}function I(t){if("object"!==e(t))throw new Error("Invalid Context passed to initialize PubSub (jsPDF-module)");var n={};this.subscribe=function(t,e,r){if(r=r||!1,"string"!=typeof t||"function"!=typeof e||"boolean"!=typeof r)throw new Error("Invalid arguments passed to PubSub.subscribe (jsPDF-module)");n.hasOwnProperty(t)||(n[t]={});var i=Math.random().toString(35);return n[t][i]=[e,!!r],i},this.unsubscribe=function(t){for(var e in n)if(n[e][t])return delete n[e][t],0===Object.keys(n[e]).length&&delete n[e],!0;return!1},this.publish=function(e){if(n.hasOwnProperty(e)){var a=Array.prototype.slice.call(arguments,1),o=[];for(var s in n[e]){var c=n[e][s];try{c[0].apply(t,a)}catch(t){r.console&&i.error("jsPDF PubSub Error",t.message,t)}c[1]&&o.push(s)}o.length&&o.forEach(this.unsubscribe)}},this.getTopics=function(){return n}}function C(t){if(!(this instanceof C))return new C(t);var e="opacity,stroke-opacity".split(",");for(var r in t)t.hasOwnProperty(r)&&e.indexOf(r)>=0&&(this[r]=t[r]);this.id="",this.objectNumber=-1}function j(t,e){this.gState=t,this.matrix=e,this.id="",this.objectNumber=-1}function O(t,e,r,n,i){if(!(this instanceof O))return new O(t,e,r,n,i);this.type="axial"===t?2:3,this.coords=e,this.colors=r,j.call(this,n,i)}function B(t,e,r,n,i){if(!(this instanceof B))return new B(t,e,r,n,i);this.boundingBox=t,this.xStep=e,this.yStep=r,this.stream="",this.cloneIndex=0,j.call(this,n,i)}function M(t){var n,a="string"==typeof arguments[0]?arguments[0]:"p",o=arguments[1],s=arguments[2],c=arguments[3],f=[],d=1,p=16,g="S",m=null;"object"===e(t=t||{})&&(a=t.orientation,o=t.unit||o,s=t.format||s,c=t.compress||t.compressPdf||c,null!==(m=t.encryption||null)&&(m.userPassword=m.userPassword||"",m.ownerPassword=m.ownerPassword||"",m.userPermissions=m.userPermissions||[]),d="number"==typeof t.userUnit?Math.abs(t.userUnit):1,void 0!==t.precision&&(n=t.precision),void 0!==t.floatPrecision&&(p=t.floatPrecision),g=t.defaultPathOperation||"S"),f=t.filters||(!0===c?["FlateEncode"]:f),o=o||"mm",a=(""+(a||"P")).toLowerCase();var v=t.putOnlyUsedFonts||!1,b={},y={internal:{},__private__:{}};y.__private__.PubSub=I;var w="1.3",N=y.__private__.getPdfVersion=function(){return w};y.__private__.setPdfVersion=function(t){w=t};var L={a0:[2383.94,3370.39],a1:[1683.78,2383.94],a2:[1190.55,1683.78],a3:[841.89,1190.55],a4:[595.28,841.89],a5:[419.53,595.28],a6:[297.64,419.53],a7:[209.76,297.64],a8:[147.4,209.76],a9:[104.88,147.4],a10:[73.7,104.88],b0:[2834.65,4008.19],b1:[2004.09,2834.65],b2:[1417.32,2004.09],b3:[1000.63,1417.32],b4:[708.66,1000.63],b5:[498.9,708.66],b6:[354.33,498.9],b7:[249.45,354.33],b8:[175.75,249.45],b9:[124.72,175.75],b10:[87.87,124.72],c0:[2599.37,3676.54],c1:[1836.85,2599.37],c2:[1298.27,1836.85],c3:[918.43,1298.27],c4:[649.13,918.43],c5:[459.21,649.13],c6:[323.15,459.21],c7:[229.61,323.15],c8:[161.57,229.61],c9:[113.39,161.57],c10:[79.37,113.39],dl:[311.81,623.62],letter:[612,792],"government-letter":[576,756],legal:[612,1008],"junior-legal":[576,360],ledger:[1224,792],tabloid:[792,1224],"credit-card":[153,243]};y.__private__.getPageFormats=function(){return L};var A=y.__private__.getPageFormat=function(t){return L[t]};s=s||"a4";var x={COMPAT:"compat",ADVANCED:"advanced"},S=x.COMPAT;function _(){this.saveGraphicsState(),ht(new Vt(_t,0,0,-_t,0,Rr()*_t).toString()+" cm"),this.setFontSize(this.getFontSize()/_t),g="n",S=x.ADVANCED}function P(){this.restoreGraphicsState(),g="S",S=x.COMPAT}var j=y.__private__.combineFontStyleAndFontWeight=function(t,e){if("bold"==t&&"normal"==e||"bold"==t&&400==e||"normal"==t&&"italic"==e||"bold"==t&&"italic"==e)throw new Error("Invalid Combination of fontweight and fontstyle");return e&&(t=400==e||"normal"===e?"italic"===t?"italic":"normal":700!=e&&"bold"!==e||"normal"!==t?(700==e?"bold":e)+""+t:"bold"),t};y.advancedAPI=function(t){var e=S===x.COMPAT;return e&&_.call(this),"function"!=typeof t||(t(this),e&&P.call(this)),this},y.compatAPI=function(t){var e=S===x.ADVANCED;return e&&P.call(this),"function"!=typeof t||(t(this),e&&_.call(this)),this},y.isAdvancedAPI=function(){return S===x.ADVANCED};var E,q=function(t){if(S!==x.ADVANCED)throw new Error(t+" is only available in 'advanced' API mode. You need to call advancedAPI() first.")},D=y.roundToPrecision=y.__private__.roundToPrecision=function(t,e){var r=n||e;if(isNaN(t)||isNaN(r))throw new Error("Invalid argument passed to jsPDF.roundToPrecision");return t.toFixed(r).replace(/0+$/,"")};E=y.hpf=y.__private__.hpf="number"==typeof p?function(t){if(isNaN(t))throw new Error("Invalid argument passed to jsPDF.hpf");return D(t,p)}:"smart"===p?function(t){if(isNaN(t))throw new Error("Invalid argument passed to jsPDF.hpf");return D(t,t>-1&&t<1?16:5)}:function(t){if(isNaN(t))throw new Error("Invalid argument passed to jsPDF.hpf");return D(t,16)};var R=y.f2=y.__private__.f2=function(t){if(isNaN(t))throw new Error("Invalid argument passed to jsPDF.f2");return D(t,2)},T=y.__private__.f3=function(t){if(isNaN(t))throw new Error("Invalid argument passed to jsPDF.f3");return D(t,3)},U=y.scale=y.__private__.scale=function(t){if(isNaN(t))throw new Error("Invalid argument passed to jsPDF.scale");return S===x.COMPAT?t*_t:S===x.ADVANCED?t:void 0},z=function(t){return S===x.COMPAT?Rr()-t:S===x.ADVANCED?t:void 0},H=function(t){return U(z(t))};y.__private__.setPrecision=y.setPrecision=function(t){"number"==typeof parseInt(t,10)&&(n=parseInt(t,10))};var W,V="00000000000000000000000000000000",G=y.__private__.getFileId=function(){return V},Y=y.__private__.setFileId=function(t){return V=void 0!==t&&/^[a-fA-F0-9]{32}$/.test(t)?t.toUpperCase():V.split("").map((function(){return"ABCDEF0123456789".charAt(Math.floor(16*Math.random()))})).join(""),null!==m&&(Ye=new k(m.userPermissions,m.userPassword,m.ownerPassword,V)),V};y.setFileId=function(t){return Y(t),this},y.getFileId=function(){return G()};var J=y.__private__.convertDateToPDFDate=function(t){var e=t.getTimezoneOffset(),r=e<0?"+":"-",n=Math.floor(Math.abs(e/60)),i=Math.abs(e%60),a=[r,Q(n),"'",Q(i),"'"].join("");return["D:",t.getFullYear(),Q(t.getMonth()+1),Q(t.getDate()),Q(t.getHours()),Q(t.getMinutes()),Q(t.getSeconds()),a].join("")},X=y.__private__.convertPDFDateToDate=function(t){var e=parseInt(t.substr(2,4),10),r=parseInt(t.substr(6,2),10)-1,n=parseInt(t.substr(8,2),10),i=parseInt(t.substr(10,2),10),a=parseInt(t.substr(12,2),10),o=parseInt(t.substr(14,2),10);return new Date(e,r,n,i,a,o,0)},K=y.__private__.setCreationDate=function(t){var e;if(void 0===t&&(t=new Date),t instanceof Date)e=J(t);else{if(!/^D:(20[0-2][0-9]|203[0-7]|19[7-9][0-9])(0[0-9]|1[0-2])([0-2][0-9]|3[0-1])(0[0-9]|1[0-9]|2[0-3])(0[0-9]|[1-5][0-9])(0[0-9]|[1-5][0-9])(\+0[0-9]|\+1[0-4]|-0[0-9]|-1[0-1])'(0[0-9]|[1-5][0-9])'?$/.test(t))throw new Error("Invalid argument passed to jsPDF.setCreationDate");e=t}return W=e},Z=y.__private__.getCreationDate=function(t){var e=W;return"jsDate"===t&&(e=X(W)),e};y.setCreationDate=function(t){return K(t),this},y.getCreationDate=function(t){return Z(t)};var $,Q=y.__private__.padd2=function(t){return("0"+parseInt(t)).slice(-2)},tt=y.__private__.padd2Hex=function(t){return("00"+(t=t.toString())).substr(t.length)},et=0,rt=[],nt=[],it=0,at=[],ot=[],st=!1,ct=nt,ut=function(){et=0,it=0,nt=[],rt=[],at=[],Qt=Kt(),te=Kt()};y.__private__.setCustomOutputDestination=function(t){st=!0,ct=t};var lt=function(t){st||(ct=t)};y.__private__.resetCustomOutputDestination=function(){st=!1,ct=nt};var ht=y.__private__.out=function(t){return t=t.toString(),it+=t.length+1,ct.push(t),ct},ft=y.__private__.write=function(t){return ht(1===arguments.length?t.toString():Array.prototype.join.call(arguments," "))},dt=y.__private__.getArrayBuffer=function(t){for(var e=t.length,r=new ArrayBuffer(e),n=new Uint8Array(r);e--;)n[e]=t.charCodeAt(e);return r},pt=[["Helvetica","helvetica","normal","WinAnsiEncoding"],["Helvetica-Bold","helvetica","bold","WinAnsiEncoding"],["Helvetica-Oblique","helvetica","italic","WinAnsiEncoding"],["Helvetica-BoldOblique","helvetica","bolditalic","WinAnsiEncoding"],["Courier","courier","normal","WinAnsiEncoding"],["Courier-Bold","courier","bold","WinAnsiEncoding"],["Courier-Oblique","courier","italic","WinAnsiEncoding"],["Courier-BoldOblique","courier","bolditalic","WinAnsiEncoding"],["Times-Roman","times","normal","WinAnsiEncoding"],["Times-Bold","times","bold","WinAnsiEncoding"],["Times-Italic","times","italic","WinAnsiEncoding"],["Times-BoldItalic","times","bolditalic","WinAnsiEncoding"],["ZapfDingbats","zapfdingbats","normal",null],["Symbol","symbol","normal",null]];y.__private__.getStandardFonts=function(){return pt};var gt=t.fontSize||16;y.__private__.setFontSize=y.setFontSize=function(t){return gt=S===x.ADVANCED?t/_t:t,this};var mt,vt=y.__private__.getFontSize=y.getFontSize=function(){return S===x.COMPAT?gt:gt*_t},bt=t.R2L||!1;y.__private__.setR2L=y.setR2L=function(t){return bt=t,this},y.__private__.getR2L=y.getR2L=function(){return bt};var yt,wt=y.__private__.setZoomMode=function(t){var e=[void 0,null,"fullwidth","fullheight","fullpage","original"];if(/^(?:\d+\.\d*|\d*\.\d+|\d+)%$/.test(t))mt=t;else if(isNaN(t)){if(-1===e.indexOf(t))throw new Error('zoom must be Integer (e.g. 2), a percentage Value (e.g. 300%) or fullwidth, fullheight, fullpage, original. "'+t+'" is not recognized.');mt=t}else mt=parseInt(t,10)};y.__private__.getZoomMode=function(){return mt};var Nt,Lt=y.__private__.setPageMode=function(t){if(-1==[void 0,null,"UseNone","UseOutlines","UseThumbs","FullScreen"].indexOf(t))throw new Error('Page mode must be one of UseNone, UseOutlines, UseThumbs, or FullScreen. "'+t+'" is not recognized.');yt=t};y.__private__.getPageMode=function(){return yt};var At=y.__private__.setLayoutMode=function(t){if(-1==[void 0,null,"continuous","single","twoleft","tworight","two"].indexOf(t))throw new Error('Layout mode must be one of continuous, single, twoleft, tworight. "'+t+'" is not recognized.');Nt=t};y.__private__.getLayoutMode=function(){return Nt},y.__private__.setDisplayMode=y.setDisplayMode=function(t,e,r){return wt(t),At(e),Lt(r),this};var xt={title:"",subject:"",author:"",keywords:"",creator:""};y.__private__.getDocumentProperty=function(t){if(-1===Object.keys(xt).indexOf(t))throw new Error("Invalid argument passed to jsPDF.getDocumentProperty");return xt[t]},y.__private__.getDocumentProperties=function(){return xt},y.__private__.setDocumentProperties=y.setProperties=y.setDocumentProperties=function(t){for(var e in xt)xt.hasOwnProperty(e)&&t[e]&&(xt[e]=t[e]);return this},y.__private__.setDocumentProperty=function(t,e){if(-1===Object.keys(xt).indexOf(t))throw new Error("Invalid arguments passed to jsPDF.setDocumentProperty");return xt[t]=e};var St,_t,Pt,kt,Ft,It={},Ct={},jt=[],Ot={},Bt={},Mt={},Et={},qt=null,Dt=0,Rt=[],Tt=new I(y),Ut=t.hotfixes||[],zt={},Ht={},Wt=[],Vt=function t(e,r,n,i,a,o){if(!(this instanceof t))return new t(e,r,n,i,a,o);isNaN(e)&&(e=1),isNaN(r)&&(r=0),isNaN(n)&&(n=0),isNaN(i)&&(i=1),isNaN(a)&&(a=0),isNaN(o)&&(o=0),this._matrix=[e,r,n,i,a,o]};Object.defineProperty(Vt.prototype,"sx",{get:function(){return this._matrix[0]},set:function(t){this._matrix[0]=t}}),Object.defineProperty(Vt.prototype,"shy",{get:function(){return this._matrix[1]},set:function(t){this._matrix[1]=t}}),Object.defineProperty(Vt.prototype,"shx",{get:function(){return this._matrix[2]},set:function(t){this._matrix[2]=t}}),Object.defineProperty(Vt.prototype,"sy",{get:function(){return this._matrix[3]},set:function(t){this._matrix[3]=t}}),Object.defineProperty(Vt.prototype,"tx",{get:function(){return this._matrix[4]},set:function(t){this._matrix[4]=t}}),Object.defineProperty(Vt.prototype,"ty",{get:function(){return this._matrix[5]},set:function(t){this._matrix[5]=t}}),Object.defineProperty(Vt.prototype,"a",{get:function(){return this._matrix[0]},set:function(t){this._matrix[0]=t}}),Object.defineProperty(Vt.prototype,"b",{get:function(){return this._matrix[1]},set:function(t){this._matrix[1]=t}}),Object.defineProperty(Vt.prototype,"c",{get:function(){return this._matrix[2]},set:function(t){this._matrix[2]=t}}),Object.defineProperty(Vt.prototype,"d",{get:function(){return this._matrix[3]},set:function(t){this._matrix[3]=t}}),Object.defineProperty(Vt.prototype,"e",{get:function(){return this._matrix[4]},set:function(t){this._matrix[4]=t}}),Object.defineProperty(Vt.prototype,"f",{get:function(){return this._matrix[5]},set:function(t){this._matrix[5]=t}}),Object.defineProperty(Vt.prototype,"rotation",{get:function(){return Math.atan2(this.shx,this.sx)}}),Object.defineProperty(Vt.prototype,"scaleX",{get:function(){return this.decompose().scale.sx}}),Object.defineProperty(Vt.prototype,"scaleY",{get:function(){return this.decompose().scale.sy}}),Object.defineProperty(Vt.prototype,"isIdentity",{get:function(){return 1===this.sx&&(0===this.shy&&(0===this.shx&&(1===this.sy&&(0===this.tx&&0===this.ty))))}}),Vt.prototype.join=function(t){return[this.sx,this.shy,this.shx,this.sy,this.tx,this.ty].map(E).join(t)},Vt.prototype.multiply=function(t){var e=t.sx*this.sx+t.shy*this.shx,r=t.sx*this.shy+t.shy*this.sy,n=t.shx*this.sx+t.sy*this.shx,i=t.shx*this.shy+t.sy*this.sy,a=t.tx*this.sx+t.ty*this.shx+this.tx,o=t.tx*this.shy+t.ty*this.sy+this.ty;return new Vt(e,r,n,i,a,o)},Vt.prototype.decompose=function(){var t=this.sx,e=this.shy,r=this.shx,n=this.sy,i=this.tx,a=this.ty,o=Math.sqrt(t*t+e*e),s=(t/=o)*r+(e/=o)*n;r-=t*s,n-=e*s;var c=Math.sqrt(r*r+n*n);return s/=c,t*(n/=c)>16&255,i=u>>8&255,a=255&u}if(void 0===i||void 0===o&&n===i&&i===a)if("string"==typeof n)r=n+" "+s[0];else switch(t.precision){case 2:r=R(n/255)+" "+s[0];break;case 3:default:r=T(n/255)+" "+s[0]}else if(void 0===o||"object"===e(o)){if(o&&!isNaN(o.a)&&0===o.a)return r=["1.","1.","1.",s[1]].join(" ");if("string"==typeof n)r=[n,i,a,s[1]].join(" ");else switch(t.precision){case 2:r=[R(n/255),R(i/255),R(a/255),s[1]].join(" ");break;default:case 3:r=[T(n/255),T(i/255),T(a/255),s[1]].join(" ")}}else if("string"==typeof n)r=[n,i,a,o,s[2]].join(" ");else switch(t.precision){case 2:r=[R(n),R(i),R(a),R(o),s[2]].join(" ");break;case 3:default:r=[T(n),T(i),T(a),T(o),s[2]].join(" ")}return r},ne=y.__private__.getFilters=function(){return f},ie=y.__private__.putStream=function(t){var e=(t=t||{}).data||"",r=t.filters||ne(),n=t.alreadyAppliedFilters||[],i=t.addLength1||!1,a=e.length,o=t.objectId,s=function(t){return t};if(null!==m&&void 0===o)throw new Error("ObjectId must be passed to putStream for file encryption");null!==m&&(s=Ye.encryptor(o,0));var c={};!0===r&&(r=["FlateEncode"]);var u=t.additionalKeyValues||[],l=(c=void 0!==M.API.processDataByFilters?M.API.processDataByFilters(e,r):{data:e,reverseChain:[]}).reverseChain+(Array.isArray(n)?n.join(" "):n.toString());if(0!==c.data.length&&(u.push({key:"Length",value:c.data.length}),!0===i&&u.push({key:"Length1",value:a})),0!=l.length)if(l.split("/").length-1==1)u.push({key:"Filter",value:l});else{u.push({key:"Filter",value:"["+l+"]"});for(var h=0;h>"),0!==c.data.length&&(ht("stream"),ht(s(c.data)),ht("endstream"))},ae=y.__private__.putPage=function(t){var e=t.number,r=t.data,n=t.objId,i=t.contentsObjId;Zt(n,!0),ht("<>"),ht("endobj");var a=r.join("\n");return S===x.ADVANCED&&(a+="\nQ"),Zt(i,!0),ie({data:a,filters:ne(),objectId:i}),ht("endobj"),n},oe=y.__private__.putPages=function(){var t,e,r=[];for(t=1;t<=Dt;t++)Rt[t].objId=Kt(),Rt[t].contentsObjId=Kt();for(t=1;t<=Dt;t++)r.push(ae({number:t,data:ot[t],objId:Rt[t].objId,contentsObjId:Rt[t].contentsObjId,mediaBox:Rt[t].mediaBox,cropBox:Rt[t].cropBox,bleedBox:Rt[t].bleedBox,trimBox:Rt[t].trimBox,artBox:Rt[t].artBox,userUnit:Rt[t].userUnit,rootDictionaryObjId:Qt,resourceDictionaryObjId:te}));Zt(Qt,!0),ht("<>"),ht("endobj"),Tt.publish("postPutPages")},se=function(t){Tt.publish("putFont",{font:t,out:ht,newObject:Xt,putStream:ie}),!0!==t.isAlreadyPutted&&(t.objectNumber=Xt(),ht("<<"),ht("/Type /Font"),ht("/BaseFont /"+F(t.postScriptName)),ht("/Subtype /Type1"),"string"==typeof t.encoding&&ht("/Encoding /"+t.encoding),ht("/FirstChar 32"),ht("/LastChar 255"),ht(">>"),ht("endobj"))},ce=function(){for(var t in It)It.hasOwnProperty(t)&&(!1===v||!0===v&&b.hasOwnProperty(t))&&se(It[t])},ue=function(t){t.objectNumber=Xt();var e=[];e.push({key:"Type",value:"/XObject"}),e.push({key:"Subtype",value:"/Form"}),e.push({key:"BBox",value:"["+[E(t.x),E(t.y),E(t.x+t.width),E(t.y+t.height)].join(" ")+"]"}),e.push({key:"Matrix",value:"["+t.matrix.toString()+"]"});var r=t.pages[1].join("\n");ie({data:r,additionalKeyValues:e,objectId:t.objectNumber}),ht("endobj")},le=function(){for(var t in zt)zt.hasOwnProperty(t)&&ue(zt[t])},he=function(t,e){var r,n=[],i=1/(e-1);for(r=0;r<1;r+=i)n.push(r);if(n.push(1),0!=t[0].offset){var a={offset:0,color:t[0].color};t.unshift(a)}if(1!=t[t.length-1].offset){var o={offset:1,color:t[t.length-1].color};t.push(o)}for(var s="",c=0,u=0;ut[c+1].offset;)c++;var l=t[c].offset,h=(r-l)/(t[c+1].offset-l),f=t[c].color,d=t[c+1].color;s+=tt(Math.round((1-h)*f[0]+h*d[0]).toString(16))+tt(Math.round((1-h)*f[1]+h*d[1]).toString(16))+tt(Math.round((1-h)*f[2]+h*d[2]).toString(16))}return s.trim()},fe=function(t,e){e||(e=21);var r=Xt(),n=he(t.colors,e),i=[];i.push({key:"FunctionType",value:"0"}),i.push({key:"Domain",value:"[0.0 1.0]"}),i.push({key:"Size",value:"["+e+"]"}),i.push({key:"BitsPerSample",value:"8"}),i.push({key:"Range",value:"[0.0 1.0 0.0 1.0 0.0 1.0]"}),i.push({key:"Decode",value:"[0.0 1.0 0.0 1.0 0.0 1.0]"}),ie({data:n,additionalKeyValues:i,alreadyAppliedFilters:["/ASCIIHexDecode"],objectId:r}),ht("endobj"),t.objectNumber=Xt(),ht("<< /ShadingType "+t.type),ht("/ColorSpace /DeviceRGB");var a="/Coords ["+E(parseFloat(t.coords[0]))+" "+E(parseFloat(t.coords[1]))+" ";2===t.type?a+=E(parseFloat(t.coords[2]))+" "+E(parseFloat(t.coords[3])):a+=E(parseFloat(t.coords[2]))+" "+E(parseFloat(t.coords[3]))+" "+E(parseFloat(t.coords[4]))+" "+E(parseFloat(t.coords[5])),ht(a+="]"),t.matrix&&ht("/Matrix ["+t.matrix.toString()+"]"),ht("/Function "+r+" 0 R"),ht("/Extend [true true]"),ht(">>"),ht("endobj")},de=function(t,e){var r=Kt(),n=Xt();e.push({resourcesOid:r,objectOid:n}),t.objectNumber=n;var i=[];i.push({key:"Type",value:"/Pattern"}),i.push({key:"PatternType",value:"1"}),i.push({key:"PaintType",value:"1"}),i.push({key:"TilingType",value:"1"}),i.push({key:"BBox",value:"["+t.boundingBox.map(E).join(" ")+"]"}),i.push({key:"XStep",value:E(t.xStep)}),i.push({key:"YStep",value:E(t.yStep)}),i.push({key:"Resources",value:r+" 0 R"}),t.matrix&&i.push({key:"Matrix",value:"["+t.matrix.toString()+"]"}),ie({data:t.stream,additionalKeyValues:i,objectId:t.objectNumber}),ht("endobj")},pe=function(t){var e;for(e in Ot)Ot.hasOwnProperty(e)&&(Ot[e]instanceof O?fe(Ot[e]):Ot[e]instanceof B&&de(Ot[e],t))},ge=function(t){for(var e in t.objectNumber=Xt(),ht("<<"),t)switch(e){case"opacity":ht("/ca "+R(t[e]));break;case"stroke-opacity":ht("/CA "+R(t[e]))}ht(">>"),ht("endobj")},me=function(){var t;for(t in Mt)Mt.hasOwnProperty(t)&&ge(Mt[t])},ve=function(){for(var t in ht("/XObject <<"),zt)zt.hasOwnProperty(t)&&zt[t].objectNumber>=0&&ht("/"+t+" "+zt[t].objectNumber+" 0 R");Tt.publish("putXobjectDict"),ht(">>")},be=function(){Ye.oid=Xt(),ht("<<"),ht("/Filter /Standard"),ht("/V "+Ye.v),ht("/R "+Ye.r),ht("/U <"+Ye.toHexString(Ye.U)+">"),ht("/O <"+Ye.toHexString(Ye.O)+">"),ht("/P "+Ye.P),ht(">>"),ht("endobj")},ye=function(){for(var t in ht("/Font <<"),It)It.hasOwnProperty(t)&&(!1===v||!0===v&&b.hasOwnProperty(t))&&ht("/"+t+" "+It[t].objectNumber+" 0 R");ht(">>")},we=function(){if(Object.keys(Ot).length>0){for(var t in ht("/Shading <<"),Ot)Ot.hasOwnProperty(t)&&Ot[t]instanceof O&&Ot[t].objectNumber>=0&&ht("/"+t+" "+Ot[t].objectNumber+" 0 R");Tt.publish("putShadingPatternDict"),ht(">>")}},Ne=function(t){if(Object.keys(Ot).length>0){for(var e in ht("/Pattern <<"),Ot)Ot.hasOwnProperty(e)&&Ot[e]instanceof y.TilingPattern&&Ot[e].objectNumber>=0&&Ot[e].objectNumber>")}},Le=function(){if(Object.keys(Mt).length>0){var t;for(t in ht("/ExtGState <<"),Mt)Mt.hasOwnProperty(t)&&Mt[t].objectNumber>=0&&ht("/"+t+" "+Mt[t].objectNumber+" 0 R");Tt.publish("putGStateDict"),ht(">>")}},Ae=function(t){Zt(t.resourcesOid,!0),ht("<<"),ht("/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]"),ye(),we(),Ne(t.objectOid),Le(),ve(),ht(">>"),ht("endobj")},xe=function(){var t=[];ce(),me(),le(),pe(t),Tt.publish("putResources"),t.forEach(Ae),Ae({resourcesOid:te,objectOid:Number.MAX_SAFE_INTEGER}),Tt.publish("postPutResources")},Se=function(){Tt.publish("putAdditionalObjects");for(var t=0;t>8&&(c=!0);t=s.join("")}for(r=t.length;void 0===c&&0!==r;)t.charCodeAt(r-1)>>8&&(c=!0),r--;if(!c)return t;for(s=e.noBOM?[]:[254,255],r=0,n=t.length;r>8)>>8)throw new Error("Character at position "+r+" of string '"+t+"' exceeds 16bits. Cannot be encoded into UCS-2 BE");s.push(l),s.push(u-(l<<8))}return String.fromCharCode.apply(void 0,s)},Ce=y.__private__.pdfEscape=y.pdfEscape=function(t,e){return Ie(t,e).replace(/\\/g,"\\\\").replace(/\(/g,"\\(").replace(/\)/g,"\\)")},je=y.__private__.beginPage=function(t){ot[++Dt]=[],Rt[Dt]={objId:0,contentsObjId:0,userUnit:Number(d),artBox:null,bleedBox:null,cropBox:null,trimBox:null,mediaBox:{bottomLeftX:0,bottomLeftY:0,topRightX:Number(t[0]),topRightY:Number(t[1])}},Me(Dt),lt(ot[$])},Oe=function(t,e){var r,n,o;switch(a=e||a,"string"==typeof t&&(r=A(t.toLowerCase()),Array.isArray(r)&&(n=r[0],o=r[1])),Array.isArray(t)&&(n=t[0]*_t,o=t[1]*_t),isNaN(n)&&(n=s[0],o=s[1]),(n>14400||o>14400)&&(i.warn("A page in a PDF can not be wider or taller than 14400 userUnit. jsPDF limits the width/height to 14400"),n=Math.min(14400,n),o=Math.min(14400,o)),s=[n,o],a.substr(0,1)){case"l":o>n&&(s=[o,n]);break;case"p":n>o&&(s=[o,n])}je(s),pr(fr),ht(Lr),0!==kr&&ht(kr+" J"),0!==Fr&&ht(Fr+" j"),Tt.publish("addPage",{pageNumber:Dt})},Be=function(t){t>0&&t<=Dt&&(ot.splice(t,1),Rt.splice(t,1),Dt--,$>Dt&&($=Dt),this.setPage($))},Me=function(t){t>0&&t<=Dt&&($=t)},Ee=y.__private__.getNumberOfPages=y.getNumberOfPages=function(){return ot.length-1},qe=function(t,e,r){var n,a=void 0;return r=r||{},t=void 0!==t?t:It[St].fontName,e=void 0!==e?e:It[St].fontStyle,n=t.toLowerCase(),void 0!==Ct[n]&&void 0!==Ct[n][e]?a=Ct[n][e]:void 0!==Ct[t]&&void 0!==Ct[t][e]?a=Ct[t][e]:!1===r.disableWarning&&i.warn("Unable to look up font label for font '"+t+"', '"+e+"'. Refer to getFontList() for available fonts."),a||r.noFallback||null==(a=Ct.times[e])&&(a=Ct.times.normal),a},De=y.__private__.putInfo=function(){var t=Xt(),e=function(t){return t};for(var r in null!==m&&(e=Ye.encryptor(t,0)),ht("<<"),ht("/Producer ("+Ce(e("jsPDF "+M.version))+")"),xt)xt.hasOwnProperty(r)&&xt[r]&&ht("/"+r.substr(0,1).toUpperCase()+r.substr(1)+" ("+Ce(e(xt[r]))+")");ht("/CreationDate ("+Ce(e(W))+")"),ht(">>"),ht("endobj")},Re=y.__private__.putCatalog=function(t){var e=(t=t||{}).rootDictionaryObjId||Qt;switch(Xt(),ht("<<"),ht("/Type /Catalog"),ht("/Pages "+e+" 0 R"),mt||(mt="fullwidth"),mt){case"fullwidth":ht("/OpenAction [3 0 R /FitH null]");break;case"fullheight":ht("/OpenAction [3 0 R /FitV null]");break;case"fullpage":ht("/OpenAction [3 0 R /Fit]");break;case"original":ht("/OpenAction [3 0 R /XYZ null null 1]");break;default:var r=""+mt;"%"===r.substr(r.length-1)&&(mt=parseInt(mt)/100),"number"==typeof mt&&ht("/OpenAction [3 0 R /XYZ null null "+R(mt)+"]")}switch(Nt||(Nt="continuous"),Nt){case"continuous":ht("/PageLayout /OneColumn");break;case"single":ht("/PageLayout /SinglePage");break;case"two":case"twoleft":ht("/PageLayout /TwoColumnLeft");break;case"tworight":ht("/PageLayout /TwoColumnRight")}yt&&ht("/PageMode /"+yt),Tt.publish("putCatalog"),ht(">>"),ht("endobj")},Te=y.__private__.putTrailer=function(){ht("trailer"),ht("<<"),ht("/Size "+(et+1)),ht("/Root "+et+" 0 R"),ht("/Info "+(et-1)+" 0 R"),null!==m&&ht("/Encrypt "+Ye.oid+" 0 R"),ht("/ID [ <"+V+"> <"+V+"> ]"),ht(">>")},Ue=y.__private__.putHeader=function(){ht("%PDF-"+w),ht("%ºß¬à")},ze=y.__private__.putXRef=function(){var t="0000000000";ht("xref"),ht("0 "+(et+1)),ht("0000000000 65535 f ");for(var e=1;e<=et;e++){"function"==typeof rt[e]?ht((t+rt[e]()).slice(-10)+" 00000 n "):void 0!==rt[e]?ht((t+rt[e]).slice(-10)+" 00000 n "):ht("0000000000 00000 n ")}},He=y.__private__.buildDocument=function(){ut(),lt(nt),Tt.publish("buildDocument"),Ue(),oe(),Se(),xe(),null!==m&&be(),De(),Re();var t=it;return ze(),Te(),ht("startxref"),ht(""+t),ht("%%EOF"),lt(ot[$]),nt.join("\n")},We=y.__private__.getBlob=function(t){return new Blob([dt(t)],{type:"application/pdf"})},Ve=y.output=y.__private__.output=Fe((function(t,e){switch("string"==typeof(e=e||{})?e={filename:e}:e.filename=e.filename||"generated.pdf",t){case void 0:return He();case"save":y.save(e.filename);break;case"arraybuffer":return dt(He());case"blob":return We(He());case"bloburi":case"bloburl":if(void 0!==r.URL&&"function"==typeof r.URL.createObjectURL)return r.URL&&r.URL.createObjectURL(We(He()))||void 0;i.warn("bloburl is not supported by your system, because URL.createObjectURL is not supported by your browser.");break;case"datauristring":case"dataurlstring":var n="",a=He();try{n=u(a)}catch(t){n=u(unescape(encodeURIComponent(a)))}return"data:application/pdf;filename="+e.filename+";base64,"+n;case"pdfobjectnewwindow":if("[object Window]"===Object.prototype.toString.call(r)){var o="https://cdnjs.cloudflare.com/ajax/libs/pdfobject/2.1.1/pdfobject.min.js",s=' integrity="sha512-4ze/a9/4jqu+tX9dfOqJYSvyYd5M6qum/3HpCLr+/Jqf0whc37VUbkpNGHR7/8pSnCFw47T1fmIpwBV7UySh3g==" crossorigin="anonymous"';e.pdfObjectUrl&&(o=e.pdfObjectUrl,s="");var c=' + @@ -617,6 +620,10 @@

Response comparison

Local-Bench — a Companion Intelligence app for benchmarking local LLMs. + + Documentation + Discord + Private & Confidential — Property of Lifescope Inc.
From 632b17d95038472613f8b5cb73f4871524e8b3dc Mon Sep 17 00:00:00 2001 From: FzzySwtr Date: Mon, 15 Jun 2026 22:53:50 -0700 Subject: [PATCH 07/13] Pad the header area 20px so the logo is not on the edge .topbar-inner shares the .wrap class but its padding shorthand (14px 0) was overriding .wrap's horizontal padding, leaving 0px left/right and pushing the logo to the screen edge. Set padding to 20px. Co-Authored-By: Claude Opus 4.8 (1M context) --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 09ef265..7e3a605 100644 --- a/index.html +++ b/index.html @@ -129,7 +129,7 @@ .topbar-inner { position: relative; display: flex; align-items: center; justify-content: space-between; - gap: 16px; padding: 14px 0; + gap: 16px; padding: 20px; } .brand { display: flex; align-items: center; gap: 12px; } .brand-logo { height: 40px; width: auto; flex: 0 0 auto; display: block; } From beb7578462a28e3b1fbaab386b230e93b133170c Mon Sep 17 00:00:00 2001 From: FzzySwtr Date: Mon, 15 Jun 2026 22:56:36 -0700 Subject: [PATCH 08/13] Header padding: 14px vertical, 20px horizontal Co-Authored-By: Claude Opus 4.8 (1M context) --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 7e3a605..ad6804a 100644 --- a/index.html +++ b/index.html @@ -129,7 +129,7 @@ .topbar-inner { position: relative; display: flex; align-items: center; justify-content: space-between; - gap: 16px; padding: 20px; + gap: 16px; padding: 14px 20px; } .brand { display: flex; align-items: center; gap: 12px; } .brand-logo { height: 40px; width: auto; flex: 0 0 auto; display: block; } From 0a28741a96a52a621b76c36eb1cb1af15711c45f Mon Sep 17 00:00:00 2001 From: FzzySwtr Date: Mon, 15 Jun 2026 23:00:38 -0700 Subject: [PATCH 09/13] Add in-app wiki-style docs with a Docs link in the main navigation - New "Docs" link in the top bar navigation; toggles an in-page docs view via the #docs hash (deep-linkable, back link returns to the dashboard) - Wiki-style page explaining what Local Bench shows and why, the types of prompts, and a brief guide to planning multi-agent systems, plus learn-more links to the CI docs and GitHub - Reuses the existing design system and light/dark theme; no emoji or dashes Co-Authored-By: Claude Opus 4.8 (1M context) --- index.html | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 88 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index ad6804a..194e29c 100644 --- a/index.html +++ b/index.html @@ -158,6 +158,14 @@ .theme-toggle .icon-dark { display: none; } [data-theme="dark"] .theme-toggle .icon-light { display: none; } [data-theme="dark"] .theme-toggle .icon-dark { display: block; } + .nav-link { + display: inline-flex; align-items: center; height: 36px; padding: 0 14px; + font-size: .82rem; font-weight: 600; color: var(--ink-2); text-decoration: none; + background: var(--surface); border: 1px solid var(--border); border-radius: 999px; + box-shadow: var(--shadow-sm); transition: background .12s ease, color .12s ease, border-color .12s ease; + } + .nav-link:hover { background: var(--surface-2); color: var(--ink); } + .nav-link.active { background: var(--accent); color: #fff; border-color: var(--accent-strong); } .pill { display: inline-flex; align-items: center; gap: 7px; font-size: .78rem; font-weight: 600; color: var(--ink-2); @@ -439,6 +447,23 @@ footer .footer-links { display: inline-flex; gap: 16px; flex-wrap: wrap; } footer .footer-links a { color: var(--ink-2); text-decoration: none; font-weight: 600; } footer .footer-links a:hover { color: var(--accent-2); text-decoration: underline; } + + /* ---- In-app docs (wiki) ---- */ + .docs { padding: 26px 0 64px; } + .docs-article { max-width: 760px; } + .docs-back { display: inline-flex; align-items: center; gap: 6px; margin-bottom: 16px; font-size: .85rem; font-weight: 600; color: var(--muted); text-decoration: none; } + .docs-back:hover { color: var(--ink); } + .docs-article h1 { font-size: 1.9rem; font-weight: 800; letter-spacing: -.02em; line-height: 1.15; } + .docs-article h2 { font-size: 1.2rem; font-weight: 700; letter-spacing: -.01em; margin-top: 34px; } + .docs-lede { font-size: 1.08rem; color: var(--muted); margin-top: 12px; line-height: 1.6; } + .docs-article p { color: var(--ink-2); line-height: 1.7; margin-top: 12px; } + .docs-article ul { margin: 12px 0 0; padding-left: 22px; color: var(--ink-2); line-height: 1.6; } + .docs-article li { margin-top: 9px; } + .docs-article li strong { color: var(--ink); } + .docs-article a { color: var(--accent-2); text-decoration: none; font-weight: 600; } + .docs-article a:hover { text-decoration: underline; } + .docs-links { list-style: none; padding-left: 0; } + .docs-links li { margin-top: 10px; } @@ -450,6 +475,7 @@
Local Bench
+ Docs Connecting…
-
+

Local LLM benchmark
& intelligence catalog

Measure throughput for the models you run locally via Ollama, and compare their skills at various tasks. Run Ollama with ollama serve in a terminal application.

@@ -473,7 +499,7 @@

Local LLM benchmark
& intelligence catalog

-
+
@@ -617,6 +643,49 @@

Response comparison

+ +
+ ← Back to dashboard +
+

Local Bench

+

Local Bench is a companion app that benchmarks local language models on your own hardware. It runs a set of prompts through models served by Ollama, measures how each one performs, and saves the results so you can compare them over time.

+ +

What it shows, and why

+

Local Bench shows how a model actually behaves on your machine:

+
    +
  • Throughput and timing for each model (how quickly it produces tokens).
  • +
  • Your system specs recorded next to every run, so a number always has context.
  • +
  • A history of runs, stored to disk, so you can compare before and after a change such as a new GPU, a different quantization, or a newer model.
  • +
+

Why it matters: a model that is fast on someone else's hardware may be too slow on yours. Before you build anything on top of a local model, Local Bench tells you what to expect, in real numbers, on the hardware you own.

+ +

Types of prompts

+

Local Bench runs a set of benchmark prompts rather than a single one, because different workloads stress a model in different ways. These categories map to how you will actually use a model:

+
    +
  • Short prompts with short answers. Closest to quick, high-frequency calls. Latency matters most here.
  • +
  • Long-context prompts. Large inputs such as documents, transcripts, or chat history test how a model handles a full context window.
  • +
  • Reasoning or multi-step prompts. Longer generations where steady throughput and stability matter.
  • +
+

Pick the prompt types that match your real use case and weigh the results accordingly.

+ +

Building multi-agent systems

+

A multi-agent system runs several models, or several copies of a model, working together: one plans, others carry out steps, another reviews. On local hardware they all share the same GPU and memory, so performance is a budget you spend.

+

Local Bench helps you plan that budget:

+
    +
  • Match models to roles. Put a small, fast model on high-frequency agents (routing, simple tool calls) and reserve a larger, slower model for the few steps that need deeper reasoning.
  • +
  • Know your ceiling. Throughput figures tell you roughly how many agents can run at once before the system slows down.
  • +
  • Re-test after changes. When you swap a model or change hardware, run the benchmark again so your agent design still fits the numbers.
  • +
  • Start small. Benchmark the one or two models you plan to use, confirm they are fast enough for the busiest agent in your system, then add more agents from there.
  • +
+ +

Learn more

+ +
+
+
Local-Bench — a Companion Intelligence app for benchmarking local LLMs. @@ -1405,7 +1474,24 @@

${escapeHtml(prompt.name)}

}); } catch (e) { /* matchMedia unavailable — stay on initial theme */ } + // ---- In-app docs routing (#docs) ---- + function showDocs(show) { + const docs = document.getElementById('docsView'); + const hero = document.getElementById('heroWrap'); + const dash = document.getElementById('dashboardMain'); + const nav = document.getElementById('docsNav'); + if (!docs || !hero || !dash) return; + docs.hidden = !show; + hero.hidden = show; + dash.hidden = show; + if (nav) nav.classList.toggle('active', show); + if (show) window.scrollTo(0, 0); + } + function applyDocsRoute() { showDocs(location.hash === '#docs'); } + window.addEventListener('hashchange', applyDocsRoute); + // Bootstrap + applyDocsRoute(); loadMeta().then(loadAvailableModels); loadSystemSpecs(); loadResults(); From b140b327d27ffd100cb7e4b9a76f07573b785a27 Mon Sep 17 00:00:00 2001 From: FzzySwtr Date: Mon, 15 Jun 2026 23:11:43 -0700 Subject: [PATCH 10/13] Restyle in-app docs to CI-Import-Tools pattern (sidebar, tab per topic) Replace the single scrolling docs page with the shared CI docs pattern: a left sidebar listing each topic as a selectable item and a content pane that shows one topic at a time. Topics: Overview, What it shows and why, Types of prompts, Building multi-agent systems, Learn more. Styling maps the CI-Import-Tools doc tokens onto the Local-Bench design system (light/dark). Co-Authored-By: Claude Opus 4.8 (1M context) --- index.html | 166 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 113 insertions(+), 53 deletions(-) diff --git a/index.html b/index.html index 194e29c..80b1f6a 100644 --- a/index.html +++ b/index.html @@ -448,22 +448,47 @@ footer .footer-links a { color: var(--ink-2); text-decoration: none; font-weight: 600; } footer .footer-links a:hover { color: var(--accent-2); text-decoration: underline; } - /* ---- In-app docs (wiki) ---- */ - .docs { padding: 26px 0 64px; } - .docs-article { max-width: 760px; } - .docs-back { display: inline-flex; align-items: center; gap: 6px; margin-bottom: 16px; font-size: .85rem; font-weight: 600; color: var(--muted); text-decoration: none; } + /* ---- In-app docs (sidebar + content, one tab per topic) ---- */ + .docs { padding: 18px 0 60px; } + .docs-layout { + display: flex; min-height: calc(100vh - 240px); + border: 1px solid var(--border); border-radius: var(--radius); + overflow: hidden; background: var(--surface); box-shadow: var(--shadow-sm); + } + .docs-sidebar { + width: 250px; flex-shrink: 0; padding: 18px 14px; + border-right: 1px solid var(--border); background: var(--surface-2); overflow-y: auto; + } + .docs-sidebar h2 { margin: 0 0 14px; font-size: .95rem; font-weight: 700; color: var(--ink); } + .docs-category { margin-bottom: 18px; } + .docs-category h3 { font-size: .7rem; text-transform: uppercase; letter-spacing: .08em; color: var(--muted); margin: 0 0 8px; font-weight: 600; } + .docs-category ul { list-style: none; padding: 0; margin: 0; } + .docs-category li { margin-bottom: 2px; } + .docs-tab { + width: 100%; text-align: left; padding: 8px 10px; border: none; background: transparent; + cursor: pointer; font-family: inherit; font-size: .88rem; font-weight: 500; color: var(--ink-2); + border-radius: 7px; transition: background .12s ease, color .12s ease; + } + .docs-tab:hover { background: var(--surface); color: var(--ink); } + .docs-tab.active { background: var(--surface); color: var(--accent-2); font-weight: 700; box-shadow: inset 2px 0 0 var(--accent); } + .docs-content { flex: 1; min-width: 0; padding: 24px 28px; overflow-y: auto; } + .docs-back { display: inline-flex; align-items: center; gap: 6px; margin-bottom: 18px; font-size: .82rem; font-weight: 600; color: var(--muted); text-decoration: none; } .docs-back:hover { color: var(--ink); } - .docs-article h1 { font-size: 1.9rem; font-weight: 800; letter-spacing: -.02em; line-height: 1.15; } - .docs-article h2 { font-size: 1.2rem; font-weight: 700; letter-spacing: -.01em; margin-top: 34px; } - .docs-lede { font-size: 1.08rem; color: var(--muted); margin-top: 12px; line-height: 1.6; } - .docs-article p { color: var(--ink-2); line-height: 1.7; margin-top: 12px; } - .docs-article ul { margin: 12px 0 0; padding-left: 22px; color: var(--ink-2); line-height: 1.6; } - .docs-article li { margin-top: 9px; } - .docs-article li strong { color: var(--ink); } - .docs-article a { color: var(--accent-2); text-decoration: none; font-weight: 600; } - .docs-article a:hover { text-decoration: underline; } + .docs-topic { max-width: 780px; } + .docs-topic h1 { font-size: 1.55rem; font-weight: 800; letter-spacing: -.02em; line-height: 1.2; margin: 0 0 12px; padding-bottom: 10px; border-bottom: 1px solid var(--border); } + .docs-topic h2 { font-size: 1.12rem; font-weight: 700; letter-spacing: -.01em; margin: 26px 0 0; } + .docs-topic p { color: var(--ink-2); line-height: 1.7; margin-top: 12px; } + .docs-topic ul { margin: 12px 0 0; padding-left: 22px; color: var(--ink-2); line-height: 1.6; } + .docs-topic li { margin-top: 9px; } + .docs-topic li strong { color: var(--ink); } + .docs-topic a { color: var(--accent-2); text-decoration: none; font-weight: 600; } + .docs-topic a:hover { text-decoration: underline; } .docs-links { list-style: none; padding-left: 0; } .docs-links li { margin-top: 10px; } + @media (max-width: 760px) { + .docs-layout { flex-direction: column; min-height: 0; } + .docs-sidebar { width: 100%; border-right: none; border-bottom: 1px solid var(--border); } + } @@ -643,47 +668,74 @@

Response comparison

- +
- ← Back to dashboard -
-

Local Bench

-

Local Bench is a companion app that benchmarks local language models on your own hardware. It runs a set of prompts through models served by Ollama, measures how each one performs, and saves the results so you can compare them over time.

- -

What it shows, and why

-

Local Bench shows how a model actually behaves on your machine:

-
    -
  • Throughput and timing for each model (how quickly it produces tokens).
  • -
  • Your system specs recorded next to every run, so a number always has context.
  • -
  • A history of runs, stored to disk, so you can compare before and after a change such as a new GPU, a different quantization, or a newer model.
  • -
-

Why it matters: a model that is fast on someone else's hardware may be too slow on yours. Before you build anything on top of a local model, Local Bench tells you what to expect, in real numbers, on the hardware you own.

- -

Types of prompts

-

Local Bench runs a set of benchmark prompts rather than a single one, because different workloads stress a model in different ways. These categories map to how you will actually use a model:

-
    -
  • Short prompts with short answers. Closest to quick, high-frequency calls. Latency matters most here.
  • -
  • Long-context prompts. Large inputs such as documents, transcripts, or chat history test how a model handles a full context window.
  • -
  • Reasoning or multi-step prompts. Longer generations where steady throughput and stability matter.
  • -
-

Pick the prompt types that match your real use case and weigh the results accordingly.

- -

Building multi-agent systems

-

A multi-agent system runs several models, or several copies of a model, working together: one plans, others carry out steps, another reviews. On local hardware they all share the same GPU and memory, so performance is a budget you spend.

-

Local Bench helps you plan that budget:

-
    -
  • Match models to roles. Put a small, fast model on high-frequency agents (routing, simple tool calls) and reserve a larger, slower model for the few steps that need deeper reasoning.
  • -
  • Know your ceiling. Throughput figures tell you roughly how many agents can run at once before the system slows down.
  • -
  • Re-test after changes. When you swap a model or change hardware, run the benchmark again so your agent design still fits the numbers.
  • -
  • Start small. Benchmark the one or two models you plan to use, confirm they are fast enough for the busiest agent in your system, then add more agents from there.
  • -
- -

Learn more

- -
+
+ +
+ ← Back to dashboard + +
+

Local Bench

+

Local Bench is a companion app that benchmarks local language models on your own hardware. It runs a set of prompts through models served by Ollama, measures how each one performs, and saves the results so you can compare them over time.

+

Everything runs locally. Your prompts and the model responses stay on your machine. Use the tabs on the left to learn what Local Bench shows, the kinds of prompts it runs, and how it helps you plan multi-agent systems.

+
+ + + + + + + + +
+
diff --git a/package.json b/package.json index cb10a53..d579281 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "performance" ], "author": "", - "license": "AGPL-3.0-only", + "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "axios": "^1.13.2", "better-sqlite3": "^12.4.6",