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
146 changes: 133 additions & 13 deletions site/changelog.html
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ <h2>Two channels</h2>
<p>The released installer is <em>SQLBI Whiteboard</em>. The pre-release installer is <em>SQLBI Whiteboard (Dev)</em>. They are separate products: different folders, different settings, different upgrade codes. Only the released channel registers <code>.wboard</code> and <code>.wimport</code>, so uninstalling Dev cannot break those associations.</p>

<h2>Published builds</h2>
<p id="release-note">Until a stable release exists, the list below is pre-release builds. Without scripting, use <a href="https://github.com/sql-bi/SQLBI-Whiteboard/releases">All releases</a>.</p>
<p id="release-note">Without scripting, use <a href="https://github.com/sql-bi/SQLBI-Whiteboard/releases">All releases</a>.</p>
<ul class="releases" id="releases">
<li><a href="https://github.com/sql-bi/SQLBI-Whiteboard/releases"><span class="ver">All releases</span><span class="when">GitHub</span><span class="go">Open</span></a></li>
</ul>
Expand Down Expand Up @@ -95,6 +95,7 @@ <h2>Published builds</h2>
(function () {
var list = document.getElementById('releases');
var note = document.getElementById('release-note');
var ISSUES = 'https://github.com/sql-bi/SQLBI-Whiteboard/issues/';

fetch('https://api.github.com/repos/sql-bi/SQLBI-Whiteboard/releases?per_page=20')
.then(function (response) {
Expand All @@ -120,13 +121,133 @@ <h2>Published builds</h2>
});
}

function firstLine(text) {
function stripTrailers(text) {
var lines = String(text).replace(/\r\n/g, '\n').split('\n');
for (var i = 0; i < lines.length; i++) {
var line = lines[i].replace(/^#+\s*/, '').trim();
if (line) return line;
while (lines.length) {
var t = lines[lines.length - 1].trim();
if (!t || /^Co-authored-by:/i.test(t) || /^Signed-off-by:/i.test(t) || /^-{3,}$/.test(t)) {
lines.pop();
continue;
}
break;
}
return lines.join('\n').trim();
}

function splitNotes(text) {
var lines = String(text).replace(/\r\n/g, '\n').split('\n');
var i = 0;
while (i < lines.length && !lines[i].trim()) i++;
var sum = (lines[i] || '').replace(/^#+\s*/, '').trim();
i++;
while (i < lines.length && !lines[i].trim()) i++;
return { sum: sum, rest: stripTrailers(lines.slice(i).join('\n')) };
}

function inline(s) {
var slots = [];
function keep(html) {
slots.push(html);
return '\0' + (slots.length - 1) + '\0';
}
s = s.replace(/`([^`]+)`/g, function (_, c) {
return keep('<code>' + c + '</code>');
});
s = s.replace(/\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)/g, function (_, t, u) {
return keep('<a href="' + u + '" rel="noopener">' + t + '</a>');
});
s = s.replace(/(^|[\s(])(https?:\/\/[^\s<]+[^.\s<),])/g, function (_, p, u) {
return p + keep('<a href="' + u + '" rel="noopener">' + u + '</a>');
});
s = s.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
s = s.replace(/(^|[\s(])\*([^*\s][^*]*)\*/g, '$1<em>$2</em>');
s = s.replace(/(^|[\s(])#(\d+)\b/g, function (_, p, n) {
return p + '<a href="' + ISSUES + n + '">#' + n + '</a>';
});
return s.replace(/\0(\d+)\0/g, function (_, i) { return slots[+i]; });
}

function md(src) {
var lines = esc(src).split('\n');
var out = [];
var i = 0;

function peek() { return i < lines.length ? lines[i] : null; }
function blank(line) { return !line || !String(line).trim(); }
function isHeading(line) { return /^(#{1,6})\s+\S/.test(line); }
function isHr(line) { return /^(-{3,}|_{3,}|\*{3,})\s*$/.test(String(line || '').trim()); }
function isUl(line) { return /^[-*+]\s+\S/.test(line); }
function isOl(line) { return /^\d+[.)]\s+\S/.test(line); }
function isFence(line) { return /^```/.test(line); }

function takeFence() {
i++;
var body = [];
while (i < lines.length && !/^```/.test(lines[i])) {
body.push(lines[i]);
i++;
}
if (i < lines.length) i++;
return '<pre><code>' + body.join('\n') + '</code></pre>';
}

function takeList(ordered) {
var tag = ordered ? 'ol' : 'ul';
var re = ordered ? /^\d+[.)]\s+/ : /^[-*+]\s+/;
var items = [];
while (i < lines.length) {
if (blank(peek())) {
var j = i + 1;
while (j < lines.length && blank(lines[j])) j++;
if (j < lines.length && re.test(lines[j])) { i = j; continue; }
break;
}
if (isFence(peek()) || isHeading(peek()) || isHr(peek())) break;
if (re.test(peek())) {
items.push(peek().replace(re, ''));
i++;
continue;
}
if (items.length) {
items[items.length - 1] += ' ' + peek().trim();
i++;
continue;
}
break;
}
return '<' + tag + '>' + items.map(function (it) {
return '<li>' + inline(it.trim()) + '</li>';
}).join('') + '</' + tag + '>';
}

function takeParagraph() {
var buf = [];
while (i < lines.length && !blank(peek())) {
if (isFence(peek()) || isHeading(peek()) || isHr(peek()) ||
isUl(peek()) || isOl(peek())) break;
buf.push(peek().trim());
i++;
}
return '<p>' + inline(buf.join(' ')) + '</p>';
}

while (i < lines.length) {
var line = peek();
if (blank(line)) { i++; continue; }
if (isFence(line)) { out.push(takeFence()); continue; }
if (isHeading(line)) {
var m = line.match(/^(#{1,6})\s+(.*)$/);
var lvl = Math.min(m[1].length, 3);
out.push('<h' + lvl + '>' + inline(m[2].trim()) + '</h' + lvl + '>');
i++;
continue;
}
if (isHr(line)) { out.push('<hr>'); i++; continue; }
if (isUl(line)) { out.push(takeList(false)); continue; }
if (isOl(line)) { out.push(takeList(true)); continue; }
out.push(takeParagraph());
}
return '';
return out.join('');
}

function notesFor(release) {
Expand All @@ -151,17 +272,16 @@ <h2>Published builds</h2>
var when = new Date(r.published_at).toLocaleDateString(undefined, {
year: 'numeric', month: 'long', day: 'numeric'
});
var full = notes[i] || '';
var sum = firstLine(full);
var parts = splitNotes(notes[i] || '');
html += '<li><div class="reltop"><a class="ver" href="' + esc(r.html_url) + '">' +
esc(r.tag_name.replace(/^v/, '')) + '</a><span class="when">' +
esc(when) + '</span></div>';
if (sum) {
html += '<p class="relsum">' + esc(sum) + '</p>';
if (parts.sum) {
html += '<p class="relsum">' + esc(parts.sum) + '</p>';
}
if (full && full.replace(/\r\n/g, '\n').trim() !== sum) {
html += '<details><summary>Notes</summary><pre class="relbody">' +
esc(full.trim()) + '</pre></details>';
if (parts.rest) {
html += '<details><summary>Notes</summary><div class="relbody">' +
md(parts.rest) + '</div></details>';
}
html += '</li>';
}
Expand Down
4 changes: 2 additions & 2 deletions site/faq.html
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,13 @@ <h2>Mac, iPad, or a browser?</h2>
<p>No. It is a Windows application. Boards are files you can keep next to the material they document; they do not open in a browser.</p>

<h2>Does it need an account, a tenant, or the internet?</h2>
<p>No account, no tenant. Boards and preferences stay on this machine. A copy that did not come from the Microsoft Store may ask GitHub once a day whether a newer released build exists; a bar under the tabs offers the download site if one does. Turn that off in <span class="ui">Help → Preferences → Updates</span>. A Microsoft Store install never makes that request. Details are on <a href="privacy.html">Privacy</a>.</p>
<p>No account, no tenant. Boards and preferences stay on this machine. A copy that did not come from the Microsoft Store may check this site once a day for a newer released build; a bar under the tabs offers the download site if one does. Turn that off in <span class="ui">Help → Preferences → Updates</span>. A Microsoft Store install never makes that request. Details are on <a href="privacy.html">Privacy</a>.</p>

<h2>Where do boards live? What is a <code>.wboard</code>?</h2>
<p>A board is a file on disk. Save writes a versioned ZIP with an embedded <code>preview.png</code>. Explorer shows that picture as the thumbnail in the released install. The <a href="https://marketplace.visualstudio.com/items?itemName=sqlbi.sqlbi-whiteboard">VS Code extension</a> shows the same picture.</p>

<h2>Can Released and Dev both be installed?</h2>
<p>Yes. The pre-release installs as <em>SQLBI Whiteboard (Dev)</em>, keeps its own settings, and does not register <code>.wboard</code> or <code>.wimport</code>, so it can sit beside a released copy without stealing the file association. Until a stable release exists, the download on the home page is the latest pre-release and says so.</p>
<p>Yes. The pre-release installs as <em>SQLBI Whiteboard (Dev)</em>, keeps its own settings, and does not register <code>.wboard</code> or <code>.wimport</code>, so it can sit beside a released copy without stealing the file association.</p>

<h2>What is LiveView, and why Reconnect after open?</h2>
<p>LiveView captures a window or a display onto the board so you can draw over it. Windows cannot save the capture permission. After you reload a board the last frame appears immediately; <span class="ui">Reconnect</span> restores the live feed. The <a href="guide.html">guide</a> has the session story.</p>
Expand Down
15 changes: 10 additions & 5 deletions site/privacy.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
<div class="wrap narrow">
<span class="eyebrow">Project</span>
<h1>Privacy</h1>
<p class="lede">The application does not create an account or send telemetry. A Microsoft Store install never phones home. Any other copy may ask GitHub once a day whether a newer released build exists; that check can be turned off.</p>
<p class="lede">The application does not send telemetry. This page is what the website and the installer actually do.</p>
</div>
</header>

Expand All @@ -46,16 +46,21 @@ <h1>Privacy</h1>
<article>
<h2>The application</h2>
<p>SQLBI Whiteboard does not create an account, does not join a tenant, and does not send telemetry. There is no Application Insights, no crash reporter, and no outbound product analytics in the source.</p>
<p>A copy that did not come from the Microsoft Store, with <span class="ui">Check for new versions</span> on (the default), makes at most one GET a day to <code>whiteboard.sqlbi.com</code> for <code>stable.json</code>, the same short file this site publishes for its own download links. A pre-release copy reads <code>dev.json</code> instead. If that request fails, it makes one to <code>github.com</code> to read the tag of the newest release. Either way it reads a version number and nothing else.</p>
<p>The request identifies the product version in the User-Agent; it does not send a machine identifier, a board, or preferences. A Microsoft Store install never makes that request; the Store updates it. Turn the check off under <span class="ui">Help → Preferences → Updates</span>.</p>
<p>A Microsoft Store install never phones home. The Store updates it. Any other copy may ask this site once a day whether a newer build exists.</p>
<ul>
<li><span class="ui">Check for new versions</span> is on by default; turn it off under <span class="ui">Help → Preferences → Updates</span>.</li>
<li>A GET of <code>stable.json</code>, or <code>dev.json</code> for a pre-release copy, the same file the download page uses.</li>
<li>If that fails, a request to <code>github.com</code> for the newest release tag.</li>
<li>The User-Agent carries the product version. Nothing else is sent.</li>
</ul>
<p>Boards are local <code>.wboard</code> files. Preferences live under <code>%APPDATA%\SQLBI\Whiteboard</code> (or <code>Whiteboard Dev</code> for the pre-release channel).</p>
<p>LiveView uses Windows Graphics Capture on this machine. The capture permission cannot be saved with the file. After you reload a board, the last frame is shown from the file; <span class="ui">Reconnect</span> asks Windows again.</p>

<h2>This website</h2>
<p>The site is static HTML on GitHub Pages. It does not set a first-party cookie and it has no consent banner.</p>
<ul>
<li>The download button on the home page calls <code>api.github.com</code> so the installer link can carry the current version. Without scripting, the same links go to the GitHub releases page.</li>
<li>The changelog page makes the same kind of request, to list published releases.</li>
<li>The download button on the home page reads <code>stable.json</code> from this site so the installer link can carry the current version. If that file is missing, it asks <code>api.github.com</code>. Without scripting, the same links go to the GitHub releases page.</li>
<li>The changelog page asks <code>api.github.com</code> to list published releases.</li>
<li>The home page embeds a player from <code>player.vimeo.com</code>, with <code>dnt=1</code>. Vimeo is a third party and may set its own cookies. There is no Vimeo JavaScript API on this site.</li>
</ul>

Expand Down
39 changes: 34 additions & 5 deletions site/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -772,7 +772,7 @@ footer a:hover { color: var(--ink); text-decoration: underline; }
background: var(--surface);
box-shadow: var(--shadow-sm);
}
.releases li { border-bottom: 1px solid var(--line); padding: 14px 18px; }
.releases li { border-bottom: 1px solid var(--line); padding: 16px 18px 18px; }
.releases li:last-child { border-bottom: 0; }
.reltop {
display: flex;
Expand All @@ -792,7 +792,7 @@ footer a:hover { color: var(--ink); text-decoration: underline; }
overflow: hidden;
text-overflow: ellipsis;
}
.releases details { margin-top: 8px; }
.releases details { margin: 10px 0 0 12px; }
.releases summary {
cursor: pointer;
color: var(--brand-link);
Expand All @@ -804,11 +804,40 @@ footer a:hover { color: var(--ink); text-decoration: underline; }
.releases summary::after { content: " +"; font-weight: 500; }
.releases details[open] summary::after { content: " \2212"; }
.relbody {
margin: 10px 0 0;
font: inherit;
font-size: 14px;
margin: 8px 0 0;
padding: 12px 16px;
background: var(--surface-2);
border-radius: 10px;
font-size: 14.5px;
line-height: 1.55;
color: var(--muted);
}
.relbody > *:first-child { margin-top: 0; }
.relbody > *:last-child { margin-bottom: 0; }
.relbody h1, .relbody h2, .relbody h3 {
font-size: 14.5px;
font-weight: 650;
color: var(--ink);
margin: 1.05em 0 .35em;
}
.relbody p { margin: 0 0 .7em; }
.relbody ul, .relbody ol { margin: 0 0 .7em; padding-left: 1.25em; }
.relbody li { margin: .2em 0; }
.relbody a { color: var(--brand-link); text-underline-offset: 2px; }
.relbody hr {
border: 0;
border-top: 1px solid var(--line);
margin: .9em 0;
}
.relbody pre {
margin: 0 0 .7em;
padding: 10px 12px;
font-size: 13px;
line-height: 1.45;
white-space: pre-wrap;
background: var(--surface);
border: 1px solid var(--line);
border-radius: 10px;
color: var(--ink);
}

Expand Down