@kurtbruns/kestrel 1.18.0 → 1.20.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.
Files changed (35) hide show
  1. package/.dev.vars.example +16 -4
  2. package/CHANGELOG.md +76 -1
  3. package/dist/public/dashboard/app.js +289 -290
  4. package/dist/public/dashboard/app.js.map +4 -4
  5. package/dist/public/dashboard/index.html +2 -2
  6. package/dist/public/dashboard/styles.css +127 -21
  7. package/dist/worker/index.js +4135 -2973
  8. package/docs/get-started/02-deploy.md +2 -2
  9. package/docs/get-started/03-access.md +17 -5
  10. package/docs/get-started/04-aws-account.md +2 -0
  11. package/docs/get-started/05-ses.md +49 -28
  12. package/docs/get-started/06-production-access.md +6 -3
  13. package/docs/get-started/07-verify.md +9 -7
  14. package/docs/get-started/08-go-live.md +12 -16
  15. package/docs/guides/01-allow-claude.md +7 -2
  16. package/docs/guides/03-notifications.md +10 -10
  17. package/docs/publishing/04-write-a-post.md +2 -2
  18. package/docs/publishing/06-troubleshooting.md +70 -21
  19. package/docs/publishing/08-connect-claude.md +1 -1
  20. package/docs/publishing/09-share-your-subscribe-link.md +3 -1
  21. package/docs/publishing/10-check-your-setup.md +13 -9
  22. package/docs/publishing/11-email-template.md +1 -1
  23. package/docs/publishing/README.md +8 -3
  24. package/docs/reference/01-configuration.md +5 -5
  25. package/migrations/0016_held_receipts.sql +26 -0
  26. package/migrations/0017_provider_account_rejected.sql +24 -0
  27. package/migrations/0018_notify_addresses.sql +13 -0
  28. package/migrations/0019_sending.sql +67 -0
  29. package/migrations/0020_notifications_not_sending.sql +40 -0
  30. package/migrations/0021_notifications_outage_receipts.sql +46 -0
  31. package/package.json +1 -1
  32. package/packages/kestrel/lib/api.mjs +28 -3
  33. package/packages/kestrel/lib/oauth.mjs +5 -3
  34. package/template/claude/CLAUDE.md +4 -2
  35. package/template/wrangler.jsonc +5 -5
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <title>Kestrel</title>
7
7
  <link rel="icon" href="/favicon.svg">
8
- <link rel="stylesheet" href="./styles.css?v=56492b07">
8
+ <link rel="stylesheet" href="./styles.css?v=00bc34db">
9
9
  <script>
10
10
  // Sidebar mode guard — set the width-based data-nav on <html> before first paint so the
11
11
  // sidebar renders in its final width with no flash. The collapse toggle's override is
@@ -116,6 +116,6 @@
116
116
  </section>
117
117
  <div id="toasts" role="status" aria-live="polite"></div>
118
118
 
119
- <script src="./app.js?v=e16b1474"></script>
119
+ <script src="./app.js?v=0af2b352"></script>
120
120
  </body>
121
121
  </html>
@@ -4767,47 +4767,149 @@ button.copy-btn.api-copy-path {
4767
4767
  }
4768
4768
 
4769
4769
  /* Setup steps: the first-run checklist, and the regular dashboard's Finish setting up card
4770
- (one component, two layouts). The card is the compact one. */
4770
+ (one component, two layouts). The card is the compact one. Finished steps fold into one
4771
+ line above the list, behind a solid check; each step left carries a small ring beside its
4772
+ title that says where it stands: plain to take, dashed while AWS has it, dotted while it
4773
+ waits on another step, solid red while a problem above is in its way. */
4771
4774
  .setup-title {
4772
4775
  margin-top: 0;
4773
4776
  }
4777
+ /* The done steps' toggle: one "N done" line that shows them in place in the list, and hides
4778
+ them again. A plain text control, so it reads as a line of the card, not a button. */
4779
+ .setup-fold {
4780
+ display: inline-flex;
4781
+ align-items: center;
4782
+ gap: 12px;
4783
+ margin: 10px 0 2px;
4784
+ padding: 2px;
4785
+ border: 0;
4786
+ background: none;
4787
+ color: var(--fg);
4788
+ font: inherit;
4789
+ font-size: 13px;
4790
+ font-weight: 600;
4791
+ cursor: pointer;
4792
+ border-radius: 6px;
4793
+ }
4794
+ .setup-fold:hover {
4795
+ background: none;
4796
+ color: var(--fg);
4797
+ }
4798
+ .setup-fold:focus-visible {
4799
+ outline: 2px solid var(--ring);
4800
+ outline-offset: 2px;
4801
+ }
4802
+ /* A chevron that points down while the done steps are hidden and up while they show. */
4803
+ .setup-fold::after {
4804
+ content: "";
4805
+ width: 6px;
4806
+ height: 6px;
4807
+ margin: -3px 0 0 -4px;
4808
+ border-right: 1.5px solid var(--muted);
4809
+ border-bottom: 1.5px solid var(--muted);
4810
+ transform: rotate(45deg);
4811
+ }
4812
+ .setup-fold[aria-expanded="true"]::after {
4813
+ margin-top: 3px;
4814
+ transform: rotate(-135deg);
4815
+ }
4774
4816
  .setup-steps {
4775
4817
  list-style: none;
4776
- counter-reset: step;
4777
4818
  margin: 8px 0 0;
4778
4819
  padding: 0;
4779
4820
  }
4821
+ /* Every row is as tall as one with a button, so a row with only a title, or no control, doesn't
4822
+ read as squeezed beside its neighbours. */
4780
4823
  .setup-steps li {
4781
- counter-increment: step;
4782
4824
  display: flex;
4783
4825
  align-items: center;
4784
- gap: 14px;
4826
+ gap: 12px;
4827
+ min-height: 59px;
4828
+ box-sizing: border-box;
4785
4829
  padding: 12px 2px;
4786
4830
  border-top: 1px solid var(--line);
4787
4831
  }
4788
4832
  .setup-steps li:first-child {
4789
4833
  border-top: 0;
4790
4834
  }
4835
+ /* The mark: beside the title's first line, whatever the hint below it. */
4836
+ .setup-fold-mark,
4791
4837
  .setup-steps li::before {
4792
- content: counter(step);
4793
4838
  flex: 0 0 auto;
4794
- width: 26px;
4795
- height: 26px;
4839
+ align-self: flex-start;
4840
+ box-sizing: border-box;
4841
+ width: 16px;
4842
+ height: 16px;
4843
+ margin-top: 2px;
4796
4844
  display: inline-flex;
4797
4845
  align-items: center;
4798
4846
  justify-content: center;
4799
4847
  border-radius: 999px;
4800
- border: 1px solid var(--line);
4801
- color: var(--muted);
4802
- font-size: 13px;
4803
- font-variant-numeric: tabular-nums;
4848
+ font-size: 10px;
4849
+ font-weight: 700;
4850
+ line-height: 1;
4804
4851
  }
4805
- /* A step already done (the list has subscribers): a check in place of its number. */
4852
+ .setup-fold-mark {
4853
+ margin-top: 0;
4854
+ align-self: center;
4855
+ }
4856
+ .setup-steps li::before {
4857
+ content: "";
4858
+ border: 1.5px solid var(--muted);
4859
+ }
4860
+ /* A step with only a title: the mark sits level with it, as the title does with its button. */
4861
+ .setup-steps li:not(:has(.setup-hint))::before {
4862
+ align-self: center;
4863
+ margin-top: 0;
4864
+ }
4865
+ /* Done: a solid check, in the fold's line and on each step it opens to. */
4866
+ .setup-fold-mark,
4806
4867
  .setup-steps li.setup-done::before {
4807
4868
  content: "✓";
4808
- color: var(--ok-fg);
4809
- border-color: var(--ok-line);
4810
- background: var(--ok-bg);
4869
+ border: 0;
4870
+ background: var(--ok-fg);
4871
+ color: var(--card);
4872
+ }
4873
+ /* Waiting on AWS, not on the publisher. */
4874
+ .setup-steps li.setup-waiting::before {
4875
+ border-style: dashed;
4876
+ border-color: var(--accent);
4877
+ }
4878
+ /* Can't be taken until an earlier step is: muted, its hint the step it waits for. Its ring is
4879
+ ten even dashes drawn by an SVG mask over the muted color, since a dotted border this small
4880
+ renders as faint specks that vary with the screen. */
4881
+ .setup-steps li.setup-blocked::before {
4882
+ border: 0;
4883
+ background: var(--muted);
4884
+ -webkit-mask:
4885
+ url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><circle cx='8' cy='8' r='7.25' fill='none' stroke='black' stroke-width='1.5' stroke-dasharray='2.4 2.155'/></svg>")
4886
+ center / 100% 100% no-repeat;
4887
+ mask:
4888
+ url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><circle cx='8' cy='8' r='7.25' fill='none' stroke='black' stroke-width='1.5' stroke-dasharray='2.4 2.155'/></svg>")
4889
+ center / 100% 100% no-repeat;
4890
+ }
4891
+ .setup-steps li.setup-blocked strong {
4892
+ color: var(--muted);
4893
+ font-weight: 500;
4894
+ }
4895
+ /* A problem the health block states is in its way. */
4896
+ .setup-steps li.setup-attention::before {
4897
+ content: "!";
4898
+ color: var(--danger-on);
4899
+ background: var(--danger-fg);
4900
+ border-color: var(--danger-fg);
4901
+ }
4902
+ .setup-steps li.setup-attention .setup-hint {
4903
+ color: var(--danger-fg);
4904
+ }
4905
+ /* A failed notification is an amber health line, so the step pointing at it is amber too. */
4906
+ .setup-steps li.setup-notify.setup-attention::before {
4907
+ background: var(--warn-fg);
4908
+ border-color: var(--warn-fg);
4909
+ color: var(--card);
4910
+ }
4911
+ .setup-steps li.setup-notify.setup-attention .setup-hint {
4912
+ color: var(--warn-fg);
4811
4913
  }
4812
4914
  .setup-step-main {
4813
4915
  flex: 1 1 auto;
@@ -4845,15 +4947,15 @@ button.copy-btn.api-copy-path {
4845
4947
  }
4846
4948
  .setup-card .setup-title {
4847
4949
  font-size: 16px;
4950
+ margin-bottom: 4px;
4951
+ }
4952
+ .setup-note {
4953
+ margin: 0;
4848
4954
  }
4849
4955
  .setup-card .setup-steps li {
4956
+ min-height: 51px;
4850
4957
  padding: 8px 2px;
4851
4958
  }
4852
- .setup-card .setup-steps li::before {
4853
- width: 22px;
4854
- height: 22px;
4855
- font-size: 12px;
4856
- }
4857
4959
 
4858
4960
  /* ============================================================
4859
4961
  Settings page. Grouped by intent, made visible: each section
@@ -5348,7 +5450,11 @@ button.set-field-remove {
5348
5450
  .settings .set-warn {
5349
5451
  color: var(--warn-fg);
5350
5452
  }
5351
- /* A notification channel that has stopped delivering: the one red in the settings reflection. */
5453
+ /* A provider warning that stops every email: red, beside the amber ones (DESIGN §8). */
5454
+ .settings .set-error {
5455
+ color: var(--danger-fg);
5456
+ }
5457
+ /* A notification channel that has stopped delivering: red in the settings reflection. */
5352
5458
  .set-pill.danger {
5353
5459
  background: var(--danger-bg);
5354
5460
  color: var(--danger-fg);