@lchase/kiln 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/assets/serve.css CHANGED
@@ -44,6 +44,7 @@
44
44
  --logo: 28px;
45
45
  --dot: 8px;
46
46
  --icon: 16px;
47
+ --icon-stroke: 2;
47
48
  --badge: 20px;
48
49
  --focus-width: 2px;
49
50
  --focus-offset: 2px;
@@ -426,7 +427,7 @@ h3 {
426
427
  position: sticky;
427
428
  top: 0;
428
429
  height: 100vh;
429
- overflow: auto;
430
+ overflow: hidden;
430
431
  background: var(--panel);
431
432
  border-right: var(--hairline) solid var(--line);
432
433
  padding: var(--space-4) var(--space-3) var(--space-5);
@@ -434,6 +435,14 @@ h3 {
434
435
  flex-direction: column;
435
436
  gap: var(--space-3);
436
437
  }
438
+ .nav-scroll {
439
+ flex: 1 1 auto;
440
+ min-height: 0;
441
+ overflow: auto;
442
+ display: flex;
443
+ flex-direction: column;
444
+ gap: var(--space-3);
445
+ }
437
446
  .brand { display: flex; align-items: center; gap: var(--space-3); padding: 0 var(--space-2); }
438
447
  .brand h1 { margin: 0; font-size: var(--text-md); line-height: var(--leading-sm); }
439
448
  .logo {
@@ -448,6 +457,22 @@ h3 {
448
457
  flex: none;
449
458
  }
450
459
  .logo svg { width: var(--icon); height: var(--icon); }
460
+ .icon {
461
+ width: var(--icon);
462
+ height: var(--icon);
463
+ flex: none;
464
+ stroke: currentColor;
465
+ fill: none;
466
+ stroke-width: var(--icon-stroke);
467
+ stroke-linecap: round;
468
+ stroke-linejoin: round;
469
+ }
470
+ #notify .icon-bell,
471
+ #pause-toggle .icon-play { display: none; }
472
+ #notify[data-state="on"] .icon-bell { display: block; }
473
+ #notify[data-state="on"] .icon-bell-off { display: none; }
474
+ #pause-toggle[aria-pressed="true"] .icon-pause { display: none; }
475
+ #pause-toggle[aria-pressed="true"] .icon-play { display: block; }
451
476
  .ver { display: block; font-size: var(--text-xs); line-height: var(--leading-xs); color: var(--faint); }
452
477
  .runstate {
453
478
  display: flex;
@@ -464,15 +489,30 @@ h3 {
464
489
  background: var(--state-running);
465
490
  }
466
491
  .live-dot.paused { background: var(--state-needs-you); }
492
+ .spend-row {
493
+ display: flex;
494
+ flex-wrap: wrap;
495
+ align-items: baseline;
496
+ gap: var(--space-2);
497
+ margin: 0;
498
+ padding: 0 var(--space-2);
499
+ }
467
500
  .spend, .cap {
468
501
  font-family: var(--font-mono);
469
502
  font-size: var(--text-xs);
470
503
  line-height: var(--leading-xs);
471
504
  color: var(--muted);
472
- padding: 0 var(--space-2);
473
505
  }
474
506
  .cap { color: var(--state-needs-you); }
475
507
  .side { display: flex; flex-direction: column; gap: var(--space-2xs); }
508
+ .side-foot {
509
+ flex: none;
510
+ display: flex;
511
+ flex-direction: column;
512
+ gap: var(--space-2);
513
+ padding-top: var(--space-3);
514
+ border-top: var(--hairline) solid var(--line);
515
+ }
476
516
  .nav-link, .nav-sub {
477
517
  display: flex;
478
518
  align-items: center;
@@ -480,12 +520,21 @@ h3 {
480
520
  gap: var(--space-2);
481
521
  padding: var(--space-2);
482
522
  border-radius: var(--radius-md);
483
- color: var(--ink);
523
+ color: var(--muted);
484
524
  text-decoration: none;
485
525
  }
486
- .nav-sub { padding-left: var(--space-4); color: var(--muted); font-size: var(--text-sm); }
487
- .nav-link:hover, .nav-sub:hover { background: var(--card); text-decoration: none; }
488
- .nav-link[aria-current="page"], .nav-sub[aria-current="page"] { background: var(--card-hover); }
526
+ .nav-label {
527
+ display: inline-flex;
528
+ align-items: center;
529
+ gap: var(--space-2);
530
+ min-width: 0;
531
+ }
532
+ .nav-sub { padding-left: calc(var(--icon) + var(--space-4)); font-size: var(--text-sm); }
533
+ .nav-link:hover, .nav-sub:hover { background: var(--card); color: var(--ink); text-decoration: none; }
534
+ .nav-link[aria-current="page"], .nav-sub[aria-current="page"] {
535
+ background: var(--card-hover);
536
+ color: var(--ink);
537
+ }
489
538
  .badge {
490
539
  min-width: var(--badge);
491
540
  height: var(--badge);
@@ -520,17 +569,47 @@ h3 {
520
569
  color: var(--ink);
521
570
  cursor: pointer;
522
571
  }
523
- .pause button { font: var(--text-xs)/var(--leading-xs) var(--font-mono); }
572
+ .pause button {
573
+ display: inline-flex;
574
+ align-items: center;
575
+ gap: var(--space-1);
576
+ font: var(--text-xs)/var(--leading-xs) var(--font-mono);
577
+ }
524
578
  .pause { margin: 0 0 0 auto; }
525
579
  .pause button[aria-pressed="true"] { color: var(--state-needs-you); }
526
- .add, .tool-link { margin-top: var(--space-1); text-align: left; }
580
+ .add { margin-top: var(--space-1); text-align: left; }
527
581
  .add, .btn.primary, .d-actions .approve button {
528
582
  background: var(--primary);
529
583
  color: var(--on-primary);
530
584
  border-color: transparent;
531
585
  font-weight: 500;
532
586
  }
533
- .tool-link { margin-top: auto; background: none; color: var(--muted); }
587
+ .notify { display: flex; flex-direction: column; gap: var(--space-1); }
588
+ .tool-link {
589
+ display: flex;
590
+ align-items: center;
591
+ gap: var(--space-2);
592
+ width: 100%;
593
+ margin: 0;
594
+ text-align: left;
595
+ background: none;
596
+ color: var(--muted);
597
+ }
598
+ .tool-link:hover { color: var(--ink); background: var(--card); }
599
+ .tool-link[data-state="on"] { color: var(--ink); }
600
+ .tool-link:disabled, .tool-link:disabled:hover {
601
+ color: var(--muted);
602
+ background: none;
603
+ cursor: not-allowed;
604
+ opacity: var(--disabled);
605
+ }
606
+ .notify-note {
607
+ margin: 0;
608
+ padding: 0 var(--space-1);
609
+ color: var(--muted);
610
+ font-size: var(--text-xs);
611
+ line-height: var(--leading-xs);
612
+ }
534
613
  .pick, .seg button, .fs-up, .fs-use, .repo-f, .wf-f {
535
614
  background: none;
536
615
  color: var(--muted);
@@ -1008,7 +1087,8 @@ body.open .drawer { transform: none; }
1008
1087
 
1009
1088
  @media (max-width: 960px) {
1010
1089
  .shell { grid-template-columns: 1fr; }
1011
- .nav { position: relative; height: auto; border-right: 0; border-bottom: var(--hairline) solid var(--line); }
1090
+ .nav { position: relative; height: auto; overflow: visible; border-right: 0; border-bottom: var(--hairline) solid var(--line); }
1091
+ .nav-scroll { overflow: visible; }
1012
1092
  .detail { grid-template-columns: 1fr; }
1013
1093
  .rail { position: static; }
1014
1094
  .d-actions .revise input { min-width: 0; width: 100%; }
package/assets/serve.js CHANGED
@@ -1,5 +1,6 @@
1
1
  (function () {
2
2
  var KEY = "kiln-needs-you";
3
+ var NOTIFY_KEY = "kiln-notify";
3
4
  var body = document.body;
4
5
  var board = document.getElementById("board");
5
6
  var count = Number(body.getAttribute("data-needs-you") || "0");
@@ -12,7 +13,7 @@
12
13
  if (raw === null) return;
13
14
  var prev = Number(raw);
14
15
  if (!isFinite(prev) || n <= prev) return;
15
- if (typeof Notification === "undefined" || Notification.permission !== "granted") return;
16
+ if (notifyMode() !== "on") return;
16
17
  var added = n - prev;
17
18
  try {
18
19
  new Notification("Kiln", { body: added === 1 ? "1 job needs you" : added + " jobs need you" });
@@ -257,24 +258,63 @@
257
258
  try { localStorage.removeItem("kiln-drawer"); } catch (err) { /* private mode */ }
258
259
  }
259
260
 
260
- function notifyLabel() {
261
+ var notifyAsking = false;
262
+
263
+ function notifyPref() {
264
+ try { return localStorage.getItem(NOTIFY_KEY) === "off" ? "off" : "on"; }
265
+ catch (err) { return "on"; }
266
+ }
267
+
268
+ function setNotifyPref(value) {
269
+ try { localStorage.setItem(NOTIFY_KEY, value); } catch (err) { /* private mode */ }
270
+ }
271
+
272
+ function notifyMode() {
273
+ if (typeof Notification === "undefined") return "unavailable";
274
+ if (Notification.permission === "denied") return "blocked";
275
+ if (Notification.permission === "granted") return notifyPref() === "off" ? "off" : "on";
276
+ return "ask";
277
+ }
278
+
279
+ function notifyCopy(mode) {
280
+ if (mode === "unavailable") return { label: "Notifications unavailable", note: "" };
281
+ if (mode === "blocked") return { label: "Notifications blocked", note: "Allow notifications for this site in the browser." };
282
+ if (mode === "on") return { label: "Notifications on", note: "" };
283
+ return { label: "Notify me", note: "" };
284
+ }
285
+
286
+ function renderNotify() {
261
287
  var button = document.getElementById("notify");
262
288
  if (!button) return;
263
- if (typeof Notification === "undefined") {
264
- button.hidden = true;
265
- return;
266
- }
289
+ var mode = notifyMode();
290
+ var copy = notifyCopy(mode);
267
291
  button.hidden = false;
268
- if (Notification.permission === "granted") {
269
- button.setAttribute("data-tip", "Notifications on");
270
- button.setAttribute("aria-label", "Notifications on");
271
- } else if (Notification.permission === "denied") {
272
- button.setAttribute("data-tip", "Notifications blocked");
273
- button.setAttribute("aria-label", "Notifications blocked");
274
- } else {
275
- button.setAttribute("data-tip", "Notify me");
276
- button.setAttribute("aria-label", "Notify me");
277
- }
292
+ button.disabled = mode === "unavailable";
293
+ button.setAttribute("data-state", mode);
294
+ button.setAttribute("aria-pressed", mode === "on" ? "true" : "false");
295
+ button.setAttribute("aria-label", copy.label);
296
+ if (copy.note) button.setAttribute("aria-describedby", "notify-note");
297
+ else button.removeAttribute("aria-describedby");
298
+ var label = document.getElementById("notify-label");
299
+ if (label) label.textContent = copy.label;
300
+ var note = document.getElementById("notify-note");
301
+ if (note) {
302
+ note.hidden = !copy.note;
303
+ note.textContent = copy.note;
304
+ }
305
+ }
306
+
307
+ function askNotify() {
308
+ if (notifyAsking || typeof Notification === "undefined" || !Notification.requestPermission) return;
309
+ notifyAsking = true;
310
+ var pending = Notification.requestPermission();
311
+ var done = function () {
312
+ notifyAsking = false;
313
+ if (typeof Notification !== "undefined" && Notification.permission === "granted") setNotifyPref("on");
314
+ renderNotify();
315
+ };
316
+ if (pending && typeof pending.then === "function") pending.then(done, done);
317
+ else done();
278
318
  }
279
319
 
280
320
  var titleDirty = false;
@@ -1490,8 +1530,19 @@
1490
1530
  return;
1491
1531
  }
1492
1532
  if (target.closest("#notify")) {
1493
- if (typeof Notification === "undefined" || Notification.permission !== "default") return;
1494
- Notification.requestPermission().then(notifyLabel);
1533
+ var mode = notifyMode();
1534
+ if (mode === "unavailable" || mode === "blocked") return;
1535
+ if (mode === "on") {
1536
+ setNotifyPref("off");
1537
+ renderNotify();
1538
+ return;
1539
+ }
1540
+ if (mode === "off") {
1541
+ setNotifyPref("on");
1542
+ renderNotify();
1543
+ return;
1544
+ }
1545
+ askNotify();
1495
1546
  return;
1496
1547
  }
1497
1548
  var act = target.closest("[data-act]");
@@ -1615,7 +1666,8 @@
1615
1666
  var toggle = document.getElementById("pause-toggle");
1616
1667
  var form = document.getElementById("pause-form");
1617
1668
  if (toggle && form && data && typeof data.paused === "boolean") {
1618
- toggle.textContent = data.paused ? "Resume" : "Pause";
1669
+ var pauseLabel = document.getElementById("pause-label");
1670
+ if (pauseLabel) pauseLabel.textContent = data.paused ? "Resume" : "Pause";
1619
1671
  toggle.setAttribute("aria-pressed", data.paused ? "true" : "false");
1620
1672
  form.setAttribute("action", data.paused ? "/api/resume" : "/api/pause");
1621
1673
  var label = document.getElementById("run-label");
@@ -1770,7 +1822,7 @@
1770
1822
  showFresh();
1771
1823
  syncValidity();
1772
1824
  renderCompact();
1773
- notifyLabel();
1825
+ renderNotify();
1774
1826
  applyRepo();
1775
1827
  stamp(true);
1776
1828
  paintUnseen();
package/dist/icons.js ADDED
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Board icons from Lucide 1.70.0 (ISC, copyright Lucide Icons and Contributors).
3
+ * https://lucide.dev — https://github.com/lucide-icons/lucide
4
+ *
5
+ * These are the official single-stroke paths. Size, stroke, and color come from
6
+ * CSS (`.icon`: `--icon`, `--icon-stroke`, `currentColor`). No fill.
7
+ */
8
+ export const ICONS = {
9
+ pipeline: `<rect width="18" height="18" x="3" y="3" rx="2"/><path d="M9 3v18"/><path d="M15 3v18"/>`,
10
+ inbox: `<polyline points="22 12 16 12 14 15 10 15 8 12 2 12"/><path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"/>`,
11
+ hand: `<path d="M11 12h2a2 2 0 1 0 0-4h-3c-.6 0-1.1.2-1.4.6L3 14"/><path d="m7 18 1.6-1.4c.3-.4.8-.6 1.4-.6h4c1.1 0 2.1-.4 2.8-1.2l4.6-4.4a2 2 0 0 0-2.75-2.91l-4.2 3.9"/><path d="m2 13 6 6"/>`,
12
+ repos: `<path d="M18 19a5 5 0 0 1-5-5v8"/><path d="M9 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v5"/><circle cx="13" cy="12" r="2"/><circle cx="20" cy="19" r="2"/>`,
13
+ history: `<path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/><path d="M12 7v5l4 2"/>`,
14
+ stats: `<path d="M3 3v16a2 2 0 0 0 2 2h16"/><path d="M18 17V9"/><path d="M13 17V5"/><path d="M8 17v-3"/>`,
15
+ today: `<path d="M8 2v3"/><path d="M16 2v3"/><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18"/>`,
16
+ triggers: `<path d="M15.914 4a1.5 1.5 0 00-2.474-1.561l-9 9A1.5 1.5 0 005.5 14h4.002a.5.5 0 01.471.666L8.086 20a1.5 1.5 0 002.475 1.56l9-9A1.5 1.5 0 0018.5 10h-3.997a.5.5 0 01-.472-.667z"/>`,
17
+ settings: `<path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915"/><circle cx="12" cy="12" r="3"/>`,
18
+ pause: `<rect x="14" y="3" width="5" height="18" rx="1"/><rect x="5" y="3" width="5" height="18" rx="1"/>`,
19
+ play: `<path d="M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z"/>`,
20
+ bell: `<path d="M10.268 21a2 2 0 0 0 3.464 0"/><path d="M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326"/>`,
21
+ "bell-off": `<path d="M10.268 21a2 2 0 0 0 3.464 0"/><path d="M17 17H4a1 1 0 0 1-.74-1.673C4.59 13.956 6 12.499 6 8a6 6 0 0 1 .258-1.742"/><path d="m2 2 20 20"/><path d="M8.668 3.01A6 6 0 0 1 18 8c0 2.687.77 4.653 1.707 6.05"/>`,
22
+ };
23
+ /** Lucide names for the marks above. Repo sublinks stay text and are not listed. */
24
+ export const ICON_LUCIDE = {
25
+ pipeline: "columns-3",
26
+ inbox: "inbox",
27
+ hand: "hand-helping",
28
+ repos: "folder-git-2",
29
+ history: "history",
30
+ stats: "chart-column",
31
+ today: "calendar",
32
+ triggers: "zap",
33
+ settings: "settings",
34
+ pause: "pause",
35
+ play: "play",
36
+ bell: "bell",
37
+ "bell-off": "bell-off",
38
+ };
39
+ export function icon(name, className = "icon") {
40
+ return `<svg class="${className}" viewBox="0 0 24 24" aria-hidden="true">${ICONS[name]}</svg>`;
41
+ }
@@ -3,6 +3,7 @@ import { join } from "node:path";
3
3
  import { packageRoot } from "./paths.js";
4
4
  import { buildBoard, } from "./board-view.js";
5
5
  import { COLUMN_LABEL, PIPELINE_COLUMNS, formatCost, formatDuration, formatLocal, formatRelative, shortSha, stateTone, } from "./job-view.js";
6
+ import { icon } from "./icons.js";
6
7
  import { renderMarkdown } from "./markdown.js";
7
8
  import { kilnVersion } from "./project.js";
8
9
  import { formatSpend } from "./rails.js";
@@ -25,15 +26,22 @@ export function renderApp(model) {
25
26
  const body = `<div class="shell">
26
27
  <aside class="nav" aria-label="Kiln">
27
28
  <div class="brand"><span class="logo" aria-hidden="true"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12V6l5-3 5 3v6l-5 3-5-3z"/><path d="M3 6l5 3 5-3M8 9v6"/></svg></span><div><h1>Kiln</h1>${version}</div></div>
29
+ <div class="nav-scroll">
30
+ ${navHtml(model)}
31
+ <button class="add" type="button" id="add-job">Add job</button>
32
+ </div>
33
+ <footer class="side-foot">
28
34
  <div class="runstate" id="runstate">
29
35
  <span class="live-dot${paused ? " paused" : ""}" id="run-dot" aria-hidden="true"></span>
30
36
  <span id="run-label">${paused ? "Paused" : "Running"}</span>
31
- <form method="post" action="${paused ? "/api/resume" : "/api/pause"}" class="pause" id="pause-form"><input type="hidden" name="csrf" value="${escapeHtml(page.csrf)}"><button type="submit" id="pause-toggle" aria-pressed="${paused ? "true" : "false"}">${paused ? "Resume" : "Pause"}</button></form>
37
+ <form method="post" action="${paused ? "/api/resume" : "/api/pause"}" class="pause" id="pause-form"><input type="hidden" name="csrf" value="${escapeHtml(page.csrf)}"><button type="submit" id="pause-toggle" aria-pressed="${paused ? "true" : "false"}">${icon("pause", "icon icon-pause")}${icon("play", "icon icon-play")}<span id="pause-label">${paused ? "Resume" : "Pause"}</span></button></form>
32
38
  </div>
33
- ${spend}${capNote}
34
- ${navHtml(model)}
35
- <button class="add" type="button" id="add-job">Add job</button>
36
- <button class="tool-link" type="button" id="notify">Notify me</button>
39
+ <p class="spend-row">${spend}${capNote}</p>
40
+ <div class="notify">
41
+ <button class="tool-link" type="button" id="notify" data-state="ask" aria-pressed="false">${icon("bell", "icon icon-bell")}${icon("bell-off", "icon icon-bell-off")}<span id="notify-label">Notify me</span></button>
42
+ <p class="notify-note" id="notify-note" hidden></p>
43
+ </div>
44
+ </footer>
37
45
  </aside>
38
46
  <div class="workspace${model.section === "job" ? " with-rail" : ""}">
39
47
  ${mainHtml(model, page)}
@@ -87,11 +95,11 @@ export function renderMessagePage(css, title, message, theme = DEFAULT_BOARD_THE
87
95
  }
88
96
  function navHtml(model) {
89
97
  const board = model.board;
90
- const item = (href, id, label, section, badge) => {
98
+ const item = (href, id, label, section, glyph, badge) => {
91
99
  const current = model.section === section ? ` aria-current="page"` : "";
92
100
  const count = badge !== undefined && badge > 0 ? `<span class="badge">${badge}</span>` : "";
93
101
  const extra = id === "open-settings" ? ` id="open-settings"` : "";
94
- return `<a class="nav-link" href="${href}"${current}${extra}>${escapeHtml(label)}${count}</a>`;
102
+ return `<a class="nav-link" href="${href}"${current}${extra}><span class="nav-label">${icon(glyph)}${escapeHtml(label)}</span>${count}</a>`;
95
103
  };
96
104
  const repos = board.repos
97
105
  .map((group) => {
@@ -100,17 +108,17 @@ function navHtml(model) {
100
108
  })
101
109
  .join("");
102
110
  return `<nav class="side" aria-label="Sections">
103
- ${item("/", "pipeline", "Pipeline", "pipeline")}
104
- ${item("/inbox", "inbox", "Inbox", "inbox", board.inboxCount)}
111
+ ${item("/", "pipeline", "Pipeline", "pipeline", "pipeline")}
112
+ ${item("/inbox", "inbox", "Inbox", "inbox", "inbox", board.inboxCount)}
105
113
  <p class="unseen" id="inbox-unseen" hidden></p>
106
- ${item("/hand", "hand", "Needs a hand", "hand", board.handCount)}
107
- ${item("/repos", "repos", "Repos", "repos")}
114
+ ${item("/hand", "hand", "Needs a hand", "hand", "hand", board.handCount)}
115
+ ${item("/repos", "repos", "Repos", "repos", "repos")}
108
116
  ${repos}
109
- ${item("/history", "history", "History", "history", board.history.length)}
110
- ${item("/stats", "stats", "Stats", "stats")}
111
- ${item("/report", "report", "Today", "report")}
112
- ${item("/triggers", "triggers", "Triggers", "triggers")}
113
- ${item("/settings", "open-settings", "Settings", "settings")}
117
+ ${item("/history", "history", "History", "history", "history", board.history.length)}
118
+ ${item("/stats", "stats", "Stats", "stats", "stats")}
119
+ ${item("/report", "report", "Today", "report", "today")}
120
+ ${item("/triggers", "triggers", "Triggers", "triggers", "triggers")}
121
+ ${item("/settings", "open-settings", "Settings", "settings", "settings")}
114
122
  </nav>`;
115
123
  }
116
124
  function mainHtml(model, page) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lchase/kiln",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "description": "Run a workflow on any registered repo. A markdown spec is the input.",
5
5
  "license": "MIT",
6
6
  "type": "module",