Compare commits

..

1 Commits

Author SHA1 Message Date
Andrew Stryker
7706ddc76d Start Wharton page 2024-09-08 14:08:12 -07:00
26 changed files with 75 additions and 739 deletions

View File

@ -51,6 +51,7 @@ publish: build #> Publish site
--safe-links \
--times \
--delete \
--cvs-exclude \
--chmod=D755,F644 \
public/ \
${DEST}

View File

@ -6,3 +6,7 @@ draft: false
Maybe I will add a brief bio. Until then, this is just a landing page for
information about me.
{{< rawhtml >}}
<a rel="me" href="https://mastodon.sdf.org/@axs">Mastodon</a>
{{< /rawhtml >}}

57
content/about/wharton.md Normal file
View File

@ -0,0 +1,57 @@
---
title: 'Wharton CTO Program'
date: 2024-09-08T10:47:47-07:00
draft: true
---
The [Chief Technology Officer
Program](https://api.accredible.com/v1/credential-net/user_referrals/90db19b55d3d85f1319d31fe7aab9ff1/click)
equips technology leaders with business context and skills that they need to effectively
contribute to executive teams. I took program from
the [University of Pennsylvania's](https://www.upenn.edu) [Wharton Business
School](https://www.wharton.upenn.edu) between September 2023 and June 2024.
The structure was that of a core program plus three electives. Wharton awards a
certificate for the successful completion of each of these elements.
I found this program to very helpful. I learned lots.
# CTO Core Program
The core program covered:
- Technology adoption
- Alliances
- Thing 3
![Chief Technology Officer](https://api.accredible.com/v1/frontend/credential_website_embed_image/certificate/106749367)
# Scaling a Unicorn
The Scaling a Unicorn elective covered:
- Thing 1
- Thing 2
- Thing 3
![Scaling a
Unicorn](https://api.accredible.com/v1/frontend/credential_website_embed_image/certificate/106997180)
# Driving Strategic Innovation
The Driving Strategic Innovation elective covered:
- Thing 1
- Thing 2
- Thing 3
![Driving Strategic
Innovation](https://api.accredible.com/v1/frontend/credential_website_embed_image/certificate/106709316)
The Es
- Thing 1
- Thing 2
- Thing 3
![Executive Presence and
Influence](https://api.accredible.com/v1/frontend/credential_website_embed_image/certificate/103003049)

View File

@ -2,8 +2,6 @@
title: Notes
date: 2024-07-13T10:00:00-00:00
draft: false
xparams:
math: true
---
Public notes and guides.
Public notes and guides for various topics.

View File

@ -1,41 +0,0 @@
---
title: Using HTML in Markdown
date: 2025-02-25T21:21:21-08:00
draft: false
tags:
- Writing
- Markdown
- HTML
- Hugo
---
Markdown uses punctuation-based syntax to format text, drawing inspiration from
plain text email conventions. The goal is for Markdown documents to be easy to
read. For concerns that the [specification](https://commonmark.org/) does not
cover, users are free to use HTML. However, the HTML tags that rendering
engines support vary considerably. Further, some rendering engines have their
own approach to extensions, like
[shortcodes](https://gohugo.io/content-management/shortcodes/) in Hugo.
Generally, best practice is to avoid mixing Markdown and HTML, as doing so can
detract from Markdowns intended simplicity and readability.
The following items are exceptions to this rule—cases where HTML provides
functionality or control that Markdown does not.
| HTML Tag(s) | Description | Notes |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------ |
| `<details>` and `<summary>` | Create collapsible sections for hiding and revealing content. | [^HUGO] |
| `<kbd>` | Represent keyboard inputs or shortcuts. | — |
| `<abbr>` | Add tooltips to abbreviations for clarity. | [^NOHUGO] |
| `<sup>` and `<sub>` | Format text as superscript or subscript. | — |
| `<mark>` | Highlight text with a background color. | [^HUGO] |
| `<!-- ... -->` (HTML Comments) | Insert comments that wont appear in the rendered output. | [^HUGO] |
| `<img>` | Embed images with enhanced control over attributes like class, style, width, and height. | — |
| `<var>` | Represent variables, parameters, or mathematical symbols to add semantic clarity in technical documentation. | [^GH] |
| `<samp>` | Denote sample output from programs or command-line operations, helping readers distinguish between code input and output. | [^GH] |
[^NOHUGO]: Not supported in Hugo.
[^HUGO]: Supported in Hugo, but may depend on the theme.
[^GH]: Not supported in GitHub

View File

@ -1 +0,0 @@
index.md

View File

@ -1,107 +0,0 @@
---
title: Bayes' Theorem Expressed as Odds
date: 2025-03-10T20:25:00-08:00
draft: false
tags:
- Coup
- US Politics
- R
- Bayes
xparams:
math: true
share:
- mastodon
---
[Bayes' Theorem][wiki-bayes-theorem] is typically stated as:
\[
P(A \mid E) = \frac{P(E \mid A) \, P(A)}{P(E \mid A) \, P(A) + P(E \mid \neg A) \, P(\neg A)}
\]
This form explicitly shows all the pieces of Bayesian reasoning:
- The _posterior_ probability of the hypothesis \(A\), given evidence \(E\):
\(P(A \mid E)\).
- The probability _prior_ to the evidence: \(P(A)\).
- The likelihood observing the evidence if the hypothesis is true:
\(P(E \mid A)\).
- The likelihood observing the evidence if the hypothesis is _not_ true.
Equivalently, the likelihood of observing the evidence if the
_alternative_ hypothesis is true: \(P(E \mid \neg A)\).
However, the above form is not terribly convenient when making calculations
over a series of events:
\[
P(A \mid E_1, \dots, E_n) = \frac{P(E_1, \dots, E_n \mid A)\,P(A)}{P(E_1, \dots, E_n \mid A) \, P(A) + P(E_1, \dots, E_n \mid \neg A) \, P(\neg A)}
\]
While we can calculate \(P(E_1, \dots, E_n)\) via successive substitutions,
there is a more convenient approach. This involves transforming
Bayes' Theorem as follows:
We start with the probability of the hypothesis (show above), and the
probability of the complement, alternate hypothesis
\[
P( \neg A \mid E_1, \dots, E_n) = \frac{P(E_1, \dots, E_n \mid \neg A)\,P( \neg
A)}{P(E_1, \dots, E_n \mid \neg A) \, P(\neg A) + P(E_1, \dots, E_n \mid A) \, P(A)}
\]
Assuming the pieces of evidence are _[conditionally
independent][wiki-independence]_ given \(A\) (and similarly given \(\neg A\)),
we can factorize the likelihood terms:
\[ P(E_1, \dots, E_n \mid A) = \prod_{i=1}^{n} P(E_i \mid A) \]
and
\[
P(E_1, \dots, E_n \mid \neg A) = \prod_{i=1}^{n} P(E_i \mid \neg A).
\]
Substitute these into the posterior odds:
\[
\frac{P(A \mid E_1, \dots, E_n)}{P(\neg A \mid E_1, \dots, E_n)} = \frac{P(A)}{P(\neg A)} \prod_{i=1}^{n} \frac{P(E_i \mid A)}{P(E_i \mid \neg A)}.
\]
Define the _prior [odds][wiki-odds]_, as:
\[
O(A) = \frac{P(A)}{P(\neg A)}
\]
and the [_likelihood ratio_][wiki-lr] for each piece of evidence \(E_i\) as:
\[
\text{LR}_i = \frac{P(E_i \mid A)}{P(E_i \mid \neg A)}.
\]
Then, we write the _posterior odds_ and the posterior probability, our desired
result, compactly as:
\[
\begin{align}
O(A \mid E_1, \dots, E_n) &= O(A) \prod_{i=1}^{n} \text{LR}_i, \\
P(A \mid E_1, \dots, E_n) &= \frac{O(A \mid E_1, \dots, E_n)}{1 + O(A \mid E_1, \dots, E_n)}.
\end{align}
\]
Thus, we have a two-step process to compute the posterior probability:
1. Calculate the posterior odds as the prior odds times the product of the
likelihood ratios.
2. Convert the odds to probabilities.
[wiki-bayes-theorem]: https://en.wikipedia.org/wiki/Bayes%27_theorem
[wiki-independence]: https://en.wikipedia.org/wiki/Conditional_independence
[wiki-odds]: https://en.wikipedia.org/wiki/Odds
[wiki-lr]: https://en.wikipedia.org/wiki/Likelihood_function#Likelihood_ratio

View File

@ -1,275 +0,0 @@
---
title: 'Autogolpe Bayesian'
date: 2025-02-24T20:17:01-08:00
draft: true
xparams:
math: true
---
We are six weeks into a presidential administration that is clearly making
a break with past administrations. Many prominent observers like [Paul
Krugman][PK] and [Robert Reich][RR] are calling this an authoritarian
_autogolpe_, or self-coup. Of course, both of these observers are left of
center, so being skeptical of these claims is a natural--and even
prudent--reaction. After all, we have experienced 250 years of democratic
government. On the other hand, maybe they are correct. Many of the stories
company out of Washington seem alarming and the sheer number of stories is
overwhelming. The Department of Government Efficiency (DOGE) shutting down
agencies such as USAID. DOGE asking federal workers to justify the work they
performed last week in an email or risk termination. Deciding how to think
about this moment in light of emotionally charged claims and commentary is
difficult. To not be sure what to think is completely understandable.
In this post, I will do my best to walk through this moment rationally. To do
this, we will take a [Bayesian approach][Wiki-Bayesian]. I'll get into the
details below, but for now think of Bayesian analysis gives a rigorous
framework for combining disparate pieces of information. In this case, we will
combine pieces of evidence that events support or refute the hypothesis that
the administration is staging an autogolpe.
## Bayesian Overview
There are a few concepts that we need for this analysis. While the math might
look fancy at first, all this is a series of probabilities, number that range
between 0 (no change) and 1 (complete certainty).
- Initial probability of a coup in the United States. We can label this as
\( P(A) \), where \(A\) is for autrocatic, self-coup or autogolpe. We label
the probability of _not_ an autogolpe as \( P( \neg A ) \). We will refer
to this initial probability as the _prior_ probability or just prior.
- Evidence that supports or refutes the conclusion that the administration
is conducting an autogolpe. We think of evidence as objective and verifiable.
For example, Trump posting a picture of himself as a king on Instagram.
- The probability that the evidence is consistent with an autogolpe,
\( P( E \mid A) \). This is our _intrepration_ of the evidence. Our
interpretation is inherentally _subjective_. Perhaps Trump was only
joking when he made the Instagram post. Perhaps Trump was very serious.
The point is that as ae outsider we can only interpret the evidence. That is
to say, there is _uncertainty_. We use probability values to
represent the uncertainty. This term is a _likelihood_.
- The probability that the evidence is consistent with business as usual.
\( P( E \mid \neg A) \). This the alternative hypothesis, that we
are not experiencing an autogolpe. We also refer to this term as a
likelihood.
- The probability that we are in the middle of autogolpe given the evidence,
\( P( A \mid E ) \). We call this the _posterior_ probability as this
the probability after accounting for the evidence.
What we doing here is defining a framework for explicitly stating our beliefs.
Prior to inauguration day, what was the probability that we would have an
autogolpe in the United States? Maybe a 1% chance. That is, \( P( A ) \). How
likely is it that the Instagram post implies an autogolpe? Maybe 70% chance.
That is, \( P( E \mid A ) \).
Our last step is a way to put this information together. This is where we
use the Bayesian approach is define that probability that the administration
is staging an autogolpe as:
\[
P(A \mid E) = \frac{P(E \mid A) \, P(A)}{P(E \mid A) \, P(A) + P(E \mid \neg A) \, P(\neg A)}
\]
That is, the probability that we are experiencing an autogolpe is equal to
a ratio. The numerator is the probability that the evidence is consistent with
an autogolpe times our prior. The denominator is the sum of two quantities.
The first is numerator and the second is probability that the evidence is
_not_ consistent with an autogolpe times the 1 minus the prior. That is, the
probability that we would _not_ have an autogolpe prior to the evidence.
With these quantities defined, let's work through a few examples to make this
clear.
## Initial Example: Replacing Top Brass in the Military
To work an example, we need an initial probability that an autogolpe would
happen in the United States. Prior to inauguration, we might have said a very
small chance. The United States has gone almost 250 years without a coup, so
the value should be low. There are recent example of a democratic government
transforming into a dictatorship (e.g., Hungary), so the value should be not
be zero. Let's say our prior probability, \( P(A) \), is 1%.
Next, let's assess one piece of evidence. The president the Secretary of
Defense fired the Chairman of the Joint Chiefs of Staff, two Service Chairs,
and three JAGS. There is precedent for firing the Chairman of the Joint Chiefs
of Staff, for example Presidents Obama and Truman fired Generals McCrystal and
MacCarther, respectively. However, there is not precedent for a mass firing of
military leadership like this.
Under the hypothesis this that we are experiencing an autogolpe, the likelihood
that we would see evidence like this is quite. Replacing top military officers
is consistent with the behavior of an administration staging a coupe. So,
let's say \( P( E \mid A ) = 0.9 \), or a 90% chance.
Under the alternate hypothesis that we are witnessing a coup, the probability
of this event is rather low. Let's say \( P( E \mid \neg A ) = 0.05 \) or 5%
chance.
We can put this together as
\[
P(A \mid E) = \frac{0.9 \times 0.01}{0.9 \times 0.01 + 0.05 \times 0.99} \approx 0.15
\]
or a 15% chance that the administration is staging an autogolpe.
## Timeline of Evidence
```{r evidence, echo=FALSE, include=FALSE}
library(tidyverse)
knitr::opts_chunk$set(dev = 'svglite')
odds_prior <- 0.01
evidence <-
tribble(
~event, ~dtime, ~p_hypothesis, ~p_alternative,
"Firing Inspector General", ymd("2025-01-24"), 0.9, 0.05,
"Military", ymd("2025-02-23"), 0.9, 0.05,
"Instagram King", ymd("2025-02-23"), 0.9, 0.05,
) |>
arrange(dtime) |>
mutate(
lr = p_hypothesis / p_alternative,
lr_cum = cumprod(lr),
odds_post = odds_prior * lr_cum,
prob_post = odds_post / (1 + odds_post)
)
```
Let
[Bayes' Theorem as Odds]({{- relref "bayes-theorem-as-odds" -}})
Firing Inspector Generals on
`r evidence[evidence$event == "Firing Inspector Generals", "dtime"] |> pull()`:
- This
- Sources: [PBS][PBS-IG]
Mass purging of civilian workers throughout the government
P(E1A)=0.90P(E1A)=0.90: If a self-coup is being attempted, a large-scale purge is highly likely.
P(E1¬A)=0.05P(E1¬A)=0.05: In normal administrations, even with turnover, such a mass politically driven purge is very unlikely.
Likelihood Ratio (LR11): 0.90/0.05=180.90/0.05=18.
Shutting Congressionally established agencies
P(E2A)=0.80P(E2A)=0.80: An authoritarian regime might seek to eliminate institutions that check its power.
P(E2¬A)=0.01P(E2¬A)=0.01: Shutting down agencies that Congress has established is almost unheard of in standard governance.
LR22: 0.80/0.01=800.80/0.01=80.
Appointing an FBI director that has vowed to hunt political enemies
P(E3A)=0.85P(E3A)=0.85: Under a self-coup scenario, appointing a politicized law enforcement head is very plausible.
P(E3¬A)=0.02P(E3¬A)=0.02: Such an appointment would be extraordinarily unusual outside of an authoritarian context.
LR33: 0.85/0.02≈42.50.85/0.02≈42.5.
Claiming only the president and the attorney general can interpret the law
P(E4A)=0.80P(E4A)=0.80: An authoritarian leader might openly claim such exclusive authority to undermine independent interpretation of laws.
P(E4¬A)=0.05P(E4¬A)=0.05: In normal politics, such an overt claim is almost never made.
LR44: 0.80/0.05=160.80/0.05=16.
Firing the Chairman of the Joint Chiefs of Staff
P(E5A)=0.75P(E5A)=0.75: Removing top military leadership can be a sign of consolidating control, especially if done for political reasons.
P(E5¬A)=0.03P(E5¬A)=0.03: Although military leadership changes do occur, firing a long-established, nonpolitical figure for political reasons is very rare.
LR55: 0.75/0.03=250.75/0.03=25.
Refusing to comply with several court orders
P(E6A)=0.90P(E6A)=0.90: An administration intent on a self-coup might ignore judicial rulings to eliminate checks on its power.
P(E6¬A)=0.01P(E6¬A)=0.01: Refusal to comply with court orders is nearly unprecedented in a normally functioning government.
LR66: 0.90/0.01=900.90/0.01=90.
Reworking the computing systems to give the White House stronger visibility and control
P(E7A)=0.70P(E7A)=0.70: While modern administrations update technology, doing so specifically to enhance control and surveillance is more likely if one is consolidating power.
P(E7¬A)=0.10P(E7¬A)=0.10: There is a moderate chance that administrative modernization occurs for reasons unrelated to authoritarian control.
LR77: 0.70/0.10=70.70/0.10=7.
Posting pictures of the president wearing a crown on the White House social media feeds
P(E8A)=0.70P(E8A)=0.70: Using provocative symbolism can be a tool to project a monarch-like or authoritarian image under a self-coup.
P(E8¬A)=0.02P(E8¬A)=0.02: Such symbolic displays are extremely unusual in a normal democratic context.
LR88: 0.70/0.02=350.70/0.02=35.
```{r evidence_tbl, echo=FALSE}
evidence |>
select(
event, dtime, p_hypothesis, p_alternative, lr
) |>
knitr::kable(
col.names = c(
"Event",
"Date",
"\\(P(E \\mid A)\\)",
"\\(P(E \\mid \\neg A)\\)",
"\\(LR\\)"
),
escape = FALSE
)
```
```{r post_tbl, echo=FALSE}
evidence |>
select(
event, dtime, odds_post, prob_post
) |>
knitr::kable(
col.names = c(
"Event",
"Date",
"Cumulative \\(O(A \\mid E)\\)",
"Cumulative \\(P(A \\mid E)\\)"
),
escape = FALSE
)
```
In a plot:
```{r post_plot, echo=FALSE}
evidence |>
arrange(dtime) |>
ggplot(
aes(x = dtime, y = prob_post)
) +
geom_line() +
theme_minimal() +
theme(
plot.background = element_rect(fill = "transparent", color = NA),
panel.background = element_rect(fill = "transparent", color = NA)
)
```
```{r init}
prior <- 0.1
prob_support <- 0.8
prob_refute <- 0.05
( prob_support * prior ) / ( prob_support * prior + prob_refute * (1 - prior))
```
[PK]: https://paulkrugman.substack.com/p/autogolpe
[RR]: https://robertreich.substack.com/p/say-what-it-is-a-coup
[Wiki-Bayesian]: https://en.wikipedia.org/wiki/Bayesian_inference
[PBS-IG]: https://www.pbs.org/newshour/politics/trump-fires-more-than-a-dozen-independent-inspectors-general-at-government-agencies

View File

@ -16,7 +16,7 @@ params:
disableFingerprinting: true
# using Chroma highlighting...
disableHLJS: true
disableHLHS: true
# where to find articles
# https://gohugo.io/functions/collections/where/#mainsections
@ -63,36 +63,6 @@ params:
- name: email
url: "mailto:andrewjstryker@proton.me"
# sharing
ShowShareButtons: true
share:
mastodon:
include: true
title: "Share on Mastodon"
linkedin:
include: false
title: "Share on LinkedIn"
url: "https://www.linkedin.com/shareArticle?mini=true&url={{ .pagePermalink | urlquery }}&title={{ .pageTitle | urlquery }}"
twitter:
include: false
title: "Share on Twitter"
url: "https://twitter.com/intent/tweet?text={{ .pageTitle | urlquery }}&url={{ .pagePermalink | urlquery }}"
facebook:
include: false
title: "Share on Facebook"
url: "https://www.facebook.com/sharer/sharer.php?u={{ .pagePermalink | urlquery }}"
pinterest:
include: false
title: "Pin this on Pinterest"
url: "https://pinterest.com/pin/create/button/?url={{ .pagePermalink | urlquery }}&description={{ .pageTitle | urlquery }}"
whatsapp:
include: false
title: "Share on WhatsApp"
url: "https://api.whatsapp.com/send?text={{ .pageTitle | urlquery }}%20{{ .pagePermalink | urlquery }}"
tocWordCountThreshold: 300
markup:
# Chroma highlighting
@ -104,35 +74,10 @@ markup:
codeFences: true
guessSyntax: true
lineNos: true
style: monokai
#style: solarized-dark
# noClasses: false
# https://gohugo.io/content-management/syntax-highlighting/#generate-syntax-highlighter-css
goldmark:
renderhooks:
link:
enableDefault: true
extensions:
passthrough:
enable: true
delimiters:
block:
- - \[
- \]
inline:
- - \(
- \)
permalinks:
posts: "/:year/:month/:day/:slug/"
ignoreFiles:
- \.Rmd$
- \.Rmarkdown$
- _cache$
- \.knit\.md$
- \.utf8\.md$
style: solarized-dark
# https://gohugo.io/getting-started/configuration-markup/#table-of-contents
# module:
# imports:
@ -146,6 +91,14 @@ menu:
name: About
url: /about/
weight: 10
#- identifier: categories
# name: Categories
# url: /categories/
# weight: 20
#- identifier: tags
# name: Tags
# url: /tags/
# weight: 30
- identifier: posts
name: Posts
url: /posts/
@ -154,11 +107,3 @@ menu:
name: Notes
url: /notes/
weight: 50
- identifier: tags
name: Tags
url: /tags/
weight: 90
#- identifier: categories
# name: Categories
# url: /categories/
# weight: 20

View File

@ -1,4 +0,0 @@
{{ if .IsHome }}
<!-- Mastodon verification link (visually hidden) -->
<a rel="me" href="https://mastodon.sdf.org/@axs" style="position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap;"> </a>
{{ end }}

View File

@ -1,25 +0,0 @@
<!-- Conditional TOC and post content -->
{{ $threshold := .Site.Params.tocWordCountThreshold | default 300 }}
{{ $showToc := false }}
{{ if isset .Params "toc" }}
{{ $showToc = .Params.xparams.toc }}
{{ else }}
{{ $wordCount := countwords .Content }}
{{ $showToc = gt $wordCount $threshold }}
{{ end }}
{{ if $showToc }}
<nav class="table-of-contents">
{{ .TableOfContents }}
</nav>
{{ end }}
<!-- KaTeX math -->
{{ if .Params.xparams.math }}
{{ partialCached "math.html" . }}
{{ end }}
<!-- Mermaid diagrams -->
{{ if .Params.xparams.mermaid }}
{{ partialCached "mermaid.html" . }}
{{ end }}

View File

@ -1,35 +0,0 @@
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.css"
integrity="sha384-zh0CIslj+VczCZtlzBcjt5ppRcsAmDnRem7ESsYwWwg3m/OaJ2l4x7YBZl9Kxxib"
crossorigin="anonymous"
>
<script
defer
src="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.js"
integrity="sha384-Rma6DA2IPUwhNxmrB/7S3Tno0YY7sFu9WSYMCuulLhIqYSGZ2gKCJWIqhBWqMQfh"
crossorigin="anonymous">
</script>
<script
defer
src="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/contrib/auto-render.min.js"
integrity="sha384-hCXGrW6PitJEwbkoStFjeJxv+fSOOQKOPbJxSfM6G5sWZjAyWhXiTIIAmQqnlLlh"
crossorigin="anonymous"
onload="renderMathInElement(document.body);">
</script>
<script>
document.addEventListener("DOMContentLoaded", function() {
renderMathInElement(document.body, {
delimiters: [
{left: '\\[', right: '\\]', display: true}, // block
{left: '\\(', right: '\\)', display: false}, // inline
],
throwOnError : false
});
});
</script>
<style>
.katex {
font-size: 1.1em;
}
</style>

View File

@ -1,5 +0,0 @@
<!-- Mermaid JS -->
<script src="https://cdn.jsdelivr.net/npm/mermaid@11.4.1/dist/mermaid.min.js"></script>
<script>
mermaid.initialize({ startOnLoad: true });
</script>

View File

@ -1,23 +0,0 @@
{{- /* layouts/partials/share-platform-defaults.html */ -}}
{{- $raw := .Site.Params.share | default (slice) -}}
{{- $defaults := slice -}}
{{- if reflect.IsSlice $raw -}}
{{- /* If .Site.Params.share is a slice, iterate over items */ -}}
{{- range $item := $raw -}}
{{- if eq (printf "%T" $item) "string" -}}
{{- $defaults = $defaults | append $item -}}
{{- else -}}
{{- /* If the item is a map, extract its "platform" key */ -}}
{{- $defaults = $defaults | append $item.platform -}}
{{- end -}}
{{- end -}}
{{- else -}}
{{- /* If .Site.Params.share is a map, iterate over keys */ -}}
{{- range $platform, $settings := $raw -}}
{{- if $settings.include -}}
{{- $defaults = $defaults | append $platform -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- return $defaults -}}

View File

@ -1,4 +0,0 @@
<a href="{{ .url }}" title="{{ .title }}" target="_blank">
<span class="icon">{{ partial "svg.html" (dict "name" .platform) }}</span>
</a>

View File

@ -1,35 +0,0 @@
{{ $mastodonTitle := default "Share on Mastodon" .Site.Params.share.mastodon.title }}
<a href="#"
class="mastodon-share-link"
title="{{ $mastodonTitle }}">
<span class="icon">{{ partialCached "svg.html" (dict "name" "mastodon") "mastodon-icon" }}</span>
</a>
<script>
document.querySelectorAll('.mastodon-share-link').forEach(function(el) {
el.addEventListener('click', function(e) {
e.preventDefault();
const text = encodeURIComponent('{{ .pageTitle }}');
const url = encodeURIComponent('{{ .pagePermalink }}');
// Define the Mastodon URI
const mastodonUri = `web+mastodon://share?text=${text}%20${url}`;
// Fallback URL
const fallbackUrl = `https://toot.kytta.dev/?text=${text}&url=${url}`;
// Attempt to open Mastodon client, fallback after 500ms
const timeout = setTimeout(() => {
window.open(fallbackUrl, '_blank', 'noopener');
}, 500);
// Set window location to Mastodon client URI
window.location.href = mastodonUri;
// If Mastodon client opens, cancel the fallback
window.addEventListener('blur', () => {
clearTimeout(timeout);
}, {once: true});
});
});
</script>

View File

@ -1,27 +0,0 @@
{{- /* layouts/partials/share-render.html */ -}}
{{- $parent := . -}}
{{- $platforms := .platforms -}}
{{- $ctx := .context -}}
{{- if gt (len $platforms) 0 -}}
<div class="social-sharing">
{{- range $platformName := $platforms -}}
{{- $cfg := index $ctx.Site.Params.share $platformName | default dict -}}
{{- /* Merge dynamic elements with the explicit configuration */ -}}
{{- $merged := merge
(dict
"pageTitle" $ctx.Title
"pagePermalink" $ctx.Permalink
"platform" $platformName
)
$cfg
-}}
{{- $partialName := printf "share-platform-%s.html" $platformName -}}
{{- if templates.Exists (printf "partials/%s" $partialName) -}}
{{- partial $partialName $merged -}}
{{- else -}}
{{- partial "share-platform-generic.html" $merged -}}
{{- end -}}
{{- end -}}
{{ partialCached "share-style" "share-style.html" "share-style" }}
</div>
{{- end -}}

View File

@ -1,20 +0,0 @@
<style>
.social-sharing {
text-align: center;
margin-top: var(--gap);
}
.social-sharing a {
display: inline-flex;
padding: 10px;
margin: 0 0.5rem;
}
.social-sharing .icon {
width: 26px;
height: 26px;
fill: currentColor;
transition: fill 0.3s;
}
.social-sharing a:hover .icon {
fill: var(--primary);
}
</style>

View File

@ -1,16 +0,0 @@
{{- /* layouts/partials/share_icons.html */ -}}
{{- /* Check if share sharing is explicitly disabled on this page */ -}}
{{- if eq (.Params.xparams.share | default true) false -}}
{{- return -}}
{{- end -}}
{{- /* Get the default list of share sharing platforms */ -}}
{{- $platforms := partialCached "share-platform-defaults.html" . "defaultPlatforms" -}}
{{- /* Override default list if the page provides its own list */ -}}
{{- $pageSocial := .Params.xparams.share | default (slice) -}}
{{- if and $pageSocial (reflect.IsSlice $pageSocial) -}}
{{- $platforms = $pageSocial -}}
{{- end -}}
{{- partial "share-render.html" (dict "platforms" $platforms "context" .) -}}

View File

@ -1,6 +0,0 @@
{{ $align := .Get "align" | default "center" }}
<div style="text-align: {{ $align }};">
<blockquote class="mastodon-embed" data-lang="en">
<a href="{{ .Get 0 }}">View Mastodon Post</a>
</blockquote>
</div>

View File

@ -1,6 +0,0 @@
{{ $align := .Get "align" | default "center" }}
<div style="text-align: {{ $align }};">
<div class="mermaid">
{{ .Inner | safeHTML }}
</div>
</div>

View File

@ -1,18 +0,0 @@
{{ $align := .Get "align" | default "center" }}
{{ $width := .Get "width" | default 640 }}
{{ $height := .Get "height" | default 360 }}
{{ $aspectRatio := mul (div (float $height) (float $width)) 100 }}
<div style="text-align: {{ $align }};">
<div class="video-container" style="position: relative; padding-bottom: {{ printf "%.2f" $aspectRatio }}%; height: 0; overflow: hidden; max-width: {{ $width }}px;">
<iframe
src="https://player.vimeo.com/video/{{ .Get 0 }}"
width="{{ $width }}"
height="{{ $height }}"
frameborder="0"
allow="autoplay; fullscreen; picture-in-picture"
allowfullscreen
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;">
</iframe>
</div>
</div>

View File

@ -1,19 +0,0 @@
{{ $align := .Get "align" | default "center" }}
{{ $width := .Get "width" | default 560 }}
{{ $height := .Get "height" | default 315 }}
{{/* Calculate aspect ratio as a percentage. Convert to float for proper division. */}}
{{ $aspectRatio := mul (div (float $height) (float $width)) 100 }}
<div style="text-align: {{ $align }};">
<div class="video-container" style="position: relative; padding-bottom: {{ printf "%.2f" $aspectRatio }}%; height: 0; overflow: hidden; max-width: {{ $width }}px;">
<iframe
width="{{ $width }}"
height="{{ $height }}"
src="https://www.youtube.com/embed/{{ .Get 0 }}"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;">
</iframe>
</div>
</div>

View File

@ -1,2 +0,0 @@
RewriteEngine Off
DirectoryIndex index.html

@ -1 +1 @@
Subproject commit e2e1011bdecaf84d59c70fa42ff3d2c29c537b65
Subproject commit 9ea3bb0e1f3aa06ed7715e73b5fabb36323f7267