@faith-tools/bible-sdk 0.0.0-stage → 0.6.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/dist/index.js ADDED
@@ -0,0 +1,946 @@
1
+ import { a as e, c as t, i as n, l as r, n as i, o as a, r as o, s } from "./providers-D_4UrThl.js";
2
+ import { OSIS_BOOK_CODES as c, OSIS_BOOK_NAMES as l, autocompletePassage as u, formatPassageForDisplay as d, getChapterCount as f, getVerseCount as p, parsePassage as m, parseToResolverUrl as h, resolveBookAlias as g, tryParsePassage as _ } from "grab-bcv";
3
+ import { Effect as v, Exit as y, Scope as b } from "effect";
4
+ // src/dom-runtime.ts
5
+ var x = (e, t, n, r) => v.acquireRelease(v.sync(() => e.addEventListener(t, n, r)), () => v.sync(() => e.removeEventListener(t, n, r))), S = v.callback((e) => {
6
+ let t = requestAnimationFrame(() => e(v.void));
7
+ return v.sync(() => cancelAnimationFrame(t));
8
+ }), C = (e, t) => v.callback((n) => {
9
+ let r = () => n(v.void);
10
+ for (let n of t) e.addEventListener(n, r);
11
+ return v.sync(() => {
12
+ for (let n of t) e.removeEventListener(n, r);
13
+ });
14
+ }), w = "si-passage", T = "bible-sk-passage", E = {
15
+ "font-sans": "\"Inter\",sans-serif",
16
+ "font-serif": "\"Source Serif 4\",serif",
17
+ "font-mono": "ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace",
18
+ "color-black": "#000",
19
+ "color-white": "#fff",
20
+ spacing: ".25rem",
21
+ "container-sm": "24rem",
22
+ "container-md": "28rem",
23
+ "container-lg": "32rem",
24
+ "text-xs": ".75rem",
25
+ "text-xs--line-height": "calc(1/.75)",
26
+ "text-sm": ".875rem",
27
+ "text-sm--line-height": "calc(1.25/.875)",
28
+ "text-base": "1rem",
29
+ "text-base--line-height": "calc(1.5/1)",
30
+ "text-lg": "1.125rem",
31
+ "text-lg--line-height": "calc(1.75/1.125)",
32
+ "text-xl": "1.25rem",
33
+ "text-xl--line-height": "calc(1.75/1.25)",
34
+ "text-2xl": "1.5rem",
35
+ "text-2xl--line-height": "calc(2/1.5)",
36
+ "text-3xl": "1.875rem",
37
+ "text-3xl--line-height": "calc(2.25/1.875)",
38
+ "font-weight-normal": "400",
39
+ "font-weight-medium": "500",
40
+ "font-weight-bold": "700",
41
+ "tracking-widest": ".1em",
42
+ "leading-tight": "1.25",
43
+ "leading-snug": "1.375",
44
+ "leading-normal": "1.5",
45
+ "radius-2xl": "1rem",
46
+ "radius-3xl": "1.5rem",
47
+ "ease-out": "cubic-bezier(0,0,.2,1)",
48
+ "animate-spin": "spin 1s linear infinite",
49
+ "animate-pulse": "pulse 2s cubic-bezier(.4,0,.6,1)infinite",
50
+ "blur-sm": "8px",
51
+ "default-transition-duration": ".15s",
52
+ "default-transition-timing-function": "cubic-bezier(.4,0,.2,1)"
53
+ }, D = Object.keys(E), O = `
54
+ /* flow-root keeps the passage's margins inside, so its layout is identical during and after the grow.
55
+ The host takes bible.css's tokens from the page when it sets them, and the wrapper uses them. */
56
+ :host {
57
+ display: flow-root;
58
+ position: relative;
59
+ ${D.map((e) => `--yv-${e}: inherit; --si-${e}: var(--yv-${e}, ${E[e]});`).join("\n ")}
60
+ }
61
+ .scripture-root {
62
+ all: initial;
63
+ display: block;
64
+ color: inherit;
65
+ ${D.map((e) => `--yv-${e}: var(--si-${e});`).join("\n ")}
66
+ }
67
+ .scripture-root { --scripture-muted: #666; --scripture-red: #a52424; }
68
+ .scripture-root[data-theme="dark"] { --scripture-muted: #aaa; --scripture-red: #ff9090; }
69
+ .scripture-bible {
70
+ font: var(--scripture-font-size, 20px)/var(--scripture-line-height, 1.625) var(--scripture-font-family, Georgia, serif);
71
+ max-width: var(--scripture-max-width, 32rem);
72
+ overflow-wrap: break-word;
73
+ }
74
+ .scripture-bible .scripture-verse-number, .fetch-bible sup[data-v] { font: 0.65em/1 system-ui, sans-serif; color: var(--scripture-muted); }
75
+ .fetch-bible sup[data-v] { padding-inline-end: 0.3em; }
76
+ .scripture-bible.fetch-bible .fb-wj { color: var(--scripture-red); }
77
+ .scripture-bible .scripture-heading { display: block; margin: 1em 0 0.5em; font: 600 0.85em/1.5 system-ui, sans-serif; }
78
+ .scripture-bible > .scripture-heading:first-child { margin-top: 0; }
79
+ .scripture-bible .scripture-jesus { color: var(--scripture-red); }
80
+ .scripture-bible .scripture-poetry { display: block; padding-inline-start: calc(var(--indent) * 1em); }
81
+ .scripture-bible .scripture-subtitle { margin: 0 0 0.5em; font-style: italic; }
82
+ :host([verse-numbers="hide"]) .scripture-verse-number { display: none; }
83
+ :host([red-letter="off"]) .scripture-jesus { color: inherit; }
84
+ :host([headings="hide"]) .scripture-heading { display: none; }
85
+ :host([verse-numbers="hide"]) .fetch-bible sup[data-v] { display: none; }
86
+ :host([red-letter="off"]) .fetch-bible .fb-wj { color: inherit; }
87
+ :host([headings="hide"]) .fetch-bible :is(.fb-ms, .fb-ms1, .fb-ms2, .fb-ms3, .fb-ms4, .fb-mr, .fb-s, .fb-s1, .fb-s2, .fb-s3, .fb-s4, .fb-sr, .fb-r, .fb-sp, .fb-qa) { display: none; }
88
+ /* Typography comes from --scripture-* properties, which pages set like any CSS. The fallbacks
89
+ are bible.css's own values, except the font: Untitled Serif is YouVersion's Bible typeface,
90
+ but bible.css defaults to Source Serif 4. */
91
+ [data-slot="yv-bible-renderer"] {
92
+ --yv-reader-font-family: var(--scripture-font-family, "Untitled Serif", "Source Serif 4", serif);
93
+ --yv-reader-font-size: var(--scripture-font-size, 20px);
94
+ --yv-reader-line-height: var(--scripture-line-height, 1.625);
95
+ --yv-reader-max-width: var(--scripture-max-width, 65ch);
96
+ /* bible.css caps each paragraph at this width. */
97
+ --yv-container-lg: var(--scripture-max-width, 32rem);
98
+ }
99
+ /* Content switches. CSS alone applies them, so toggling one never refetches or re-renders. */
100
+ :host([verse-numbers="hide"]) .yv-vlbl { display: none; }
101
+ :host([red-letter="off"]) .wj { color: inherit; }
102
+ /* .yv-h marks editorial headings. Psalm titles (.d) are Scripture, so they stay. */
103
+ :host([headings="hide"]) .yv-h { display: none; }
104
+ /* Translators' added words read as italic, as in print Bibles. bible.css also bolds them,
105
+ which stresses the words the original text does not contain. */
106
+ [data-slot="yv-bible-renderer"] .add { font-weight: inherit; }
107
+ /* Page frameworks (Sensible UI, Pico) draw their own spinner on [aria-busy]; this element has one.
108
+ !important in a shadow root beats the page's rules for the host. */
109
+ :host([aria-busy])::before, :host([aria-busy])::after { content: none !important; }
110
+ .scripture-loading { display: flex; padding: 0.75rem 0; opacity: 0.5; }
111
+ .scripture-stage { position: absolute; inset: 0 0 auto; height: 0; overflow: hidden; visibility: hidden; }
112
+ .scripture-loading > svg { animation: scripture-spin 0.8s linear infinite; }
113
+ @keyframes scripture-spin { to { transform: rotate(360deg); } }
114
+ [role="alert"] { margin: 0; opacity: 0.7; font-family: var(--yv-font-sans, system-ui, sans-serif); }
115
+ /* Every layout uses the same citation; only its place changes. */
116
+ .scripture-citation {
117
+ all: unset;
118
+ display: block;
119
+ /* Match the width bible.css gives the text. */
120
+ max-width: var(--scripture-max-width, 32rem);
121
+ margin-block-start: 1rem;
122
+ font: calc(var(--scripture-font-size, 20px) * 0.8)/1.5 var(--yv-font-sans, system-ui, sans-serif);
123
+ color: var(--yv-foreground);
124
+ }
125
+ .scripture-root > .scripture-citation:first-child { margin-block: 0 1rem; }
126
+ .scripture-citation > .scripture-version { color: var(--yv-muted-foreground, var(--scripture-muted)); }
127
+ /* The attribution always comes after the text, right under a citation when one follows it. */
128
+ .scripture-attribution {
129
+ all: unset;
130
+ display: block;
131
+ max-width: var(--scripture-max-width, 32rem);
132
+ margin-block-start: 1rem;
133
+ font: 0.75rem/1.5 var(--yv-font-sans, system-ui, sans-serif);
134
+ color: var(--yv-muted-foreground, var(--scripture-muted));
135
+ }
136
+ .scripture-root .scripture-attribution a { color: inherit; text-decoration: none; text-underline-offset: 0.15em; }
137
+ .scripture-root .scripture-attribution a:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
138
+ @media (hover: hover) and (pointer: fine) {
139
+ .scripture-root .scripture-attribution a:hover { text-decoration: underline; }
140
+ }
141
+ .scripture-citation + .scripture-attribution { margin-block-start: 0; }
142
+ .scripture-quote {
143
+ all: unset;
144
+ display: block;
145
+ padding-inline-start: 1rem;
146
+ border-inline-start: 2px solid color-mix(in srgb, currentColor 18%, transparent);
147
+ }
148
+ @media (prefers-reduced-motion: reduce) { .scripture-loading > svg { animation-duration: 2.4s; } }`, k = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M21 12a9 9 0 1 1-6.219-8.56\"/></svg>", A = matchMedia("(prefers-reduced-motion: reduce)"), j = [
149
+ "heading",
150
+ "citation",
151
+ "quote",
152
+ "none"
153
+ ], M = class extends HTMLElement {
154
+ static observedAttributes = [
155
+ "provider",
156
+ "bible-version-id",
157
+ "yvp-public-app-key",
158
+ "yvp-api-host",
159
+ "yvp-bible-version-id",
160
+ "passage-id",
161
+ "theme",
162
+ "reference",
163
+ "direction"
164
+ ];
165
+ providerOverride;
166
+ get bibleProvider() {
167
+ return this.providerOverride;
168
+ }
169
+ set bibleProvider(e) {
170
+ this.providerOverride = e, this.isConnected && this.scheduleLoad();
171
+ }
172
+ generation = 0;
173
+ loaded = null;
174
+ scheduled = !1;
175
+ cancelLoad;
176
+ cancelTheme;
177
+ cancelAnimation;
178
+ systemDark = matchMedia("(prefers-color-scheme: dark)");
179
+ onSystemTheme = () => this.applyTheme();
180
+ root = document.createElement("div");
181
+ constructor() {
182
+ super();
183
+ let e = this.attachShadow({ mode: "open" }), t = document.createElement("style");
184
+ t.textContent = O, this.root.className = "scripture-root", e.append(t, this.root);
185
+ }
186
+ connectedCallback() {
187
+ this.cancelTheme = v.runCallback(x(this.systemDark, "change", this.onSystemTheme).pipe(v.andThen(v.never), v.scoped)), this.root.childElementCount || this.showLoading(), this.scheduleLoad();
188
+ }
189
+ disconnectedCallback() {
190
+ this.cancelTheme?.(), this.cancelAnimation?.(), this.generation += 1, this.cancelLoad?.();
191
+ }
192
+ attributeChangedCallback(e, t, n) {
193
+ t !== n && this.isConnected && (e === "theme" ? this.applyTheme() : e === "reference" || e === "direction" ? this.relayout() : this.scheduleLoad());
194
+ }
195
+ scheduleLoad() {
196
+ this.generation += 1, this.cancelLoad?.(), this.cancelAnimation?.(), this.scheduled || (this.scheduled = !0, queueMicrotask(() => {
197
+ this.scheduled = !1, this.isConnected && (this.cancelLoad = v.runCallback(this.load(this.generation).pipe(v.catch((e) => v.sync(() => {
198
+ this.showError("Unable to display Bible text. Check the provider, version, and passage."), this.dispatchEvent(new CustomEvent("scripture-error", { detail: e }));
199
+ })), v.scoped)));
200
+ }));
201
+ }
202
+ applyTheme() {
203
+ let e = this.getAttribute("theme"), t = e === "dark" || e === "auto" && this.systemDark.matches ? "dark" : "light";
204
+ this.root.dataset.theme = t;
205
+ for (let e of this.root.querySelectorAll("[data-yv-sdk]")) e.setAttribute("data-yv-theme", t);
206
+ }
207
+ get referenceLayout() {
208
+ let e = this.getAttribute("reference");
209
+ return e && j.includes(e) ? e : "heading";
210
+ }
211
+ relayout() {
212
+ this.loaded && !this.getAttribute("aria-busy") && (this.root.replaceChildren(...this.compose(this.loaded.display, this.loaded.passageId)), this.applyTheme());
213
+ }
214
+ compose(e, t) {
215
+ let n = this.ownerDocument, r = (e, t, r, i) => {
216
+ let a = n.createElement(e);
217
+ return a.className = t, a.setAttribute("part", r), i !== void 0 && (a.textContent = i), a;
218
+ }, i = _(t), a = i.ok ? d(i.value) : t, o = n.createElement("section");
219
+ o.setAttribute("part", "text");
220
+ for (let [t, n] of Object.entries(e.containerAttributes ?? {})) o.setAttribute(t, n);
221
+ let s = this.getAttribute("direction");
222
+ o.setAttribute("dir", s === "ltr" || s === "rtl" ? s : e.direction ?? "auto"), o.innerHTML = e.html;
223
+ let c = r("p", "scripture-citation", "citation");
224
+ e.yvpBibleVersion && c.setAttribute("data-yv-sdk", ""), c.append(a, " ", r("span", "scripture-version", "version", e.version.abbreviation));
225
+ let l = r("p", "scripture-attribution", "attribution");
226
+ e.yvpBibleVersion && l.setAttribute("data-yv-sdk", "");
227
+ let u = n.createElement("bdi");
228
+ switch (u.setAttribute("dir", "auto"), e.attributionHtml === void 0 ? u.textContent = e.attribution : u.innerHTML = e.attributionHtml, l.append(u), this.referenceLayout) {
229
+ case "heading": return [
230
+ c,
231
+ o,
232
+ l
233
+ ];
234
+ case "citation": return [
235
+ o,
236
+ c,
237
+ l
238
+ ];
239
+ case "quote": {
240
+ let e = r("blockquote", "scripture-quote", "quote");
241
+ return e.append(o, c, l), [e];
242
+ }
243
+ case "none": return [o, l];
244
+ }
245
+ }
246
+ showLoading() {
247
+ this.setAttribute("aria-busy", "true");
248
+ let e = this.ownerDocument.createElement("div");
249
+ e.className = "scripture-loading", e.setAttribute("part", "loader"), e.setAttribute("role", "status"), e.setAttribute("aria-label", "Loading Bible text"), e.innerHTML = k, this.root.replaceChildren(e);
250
+ }
251
+ showError(e) {
252
+ let t = this.ownerDocument.createElement("p");
253
+ t.setAttribute("role", "alert"), t.setAttribute("part", "error"), t.textContent = e, this.removeAttribute("aria-busy"), this.root.replaceChildren(t);
254
+ }
255
+ load = v.fn("scripture.passage.load")(function* (e) {
256
+ this.loaded = null;
257
+ let n = this.getAttribute("passage-id")?.trim();
258
+ if (!n) {
259
+ this.showError("Set passage-id to display Bible text.");
260
+ return;
261
+ }
262
+ this.root.querySelector(":scope > .scripture-loading") || this.showLoading();
263
+ let i = !1;
264
+ yield* S.pipe(v.andThen(v.sync(() => {
265
+ i = !0;
266
+ })), v.forkScoped);
267
+ let a = yield* r({
268
+ ...s,
269
+ provider: this.getAttribute("provider") || this.providerOverride || s.provider,
270
+ yvpPublicAppKey: this.getAttribute("yvp-public-app-key")?.trim() || s.yvpPublicAppKey,
271
+ yvpApiHost: this.getAttribute("yvp-api-host") || s.yvpApiHost
272
+ }), c = a === t(s) ? s.bibleVersionId ?? (a.id === "youversion" ? s.yvpBibleVersionId?.toString() : void 0) : void 0, l = this.getAttribute("bible-version-id") || (a.id === "youversion" ? this.getAttribute("yvp-bible-version-id") : void 0) || c || a.defaultVersionId, u = yield* o(a, l, n);
273
+ if (!this.isConnected || e !== this.generation || (yield* this.installStylesheets(u), !this.isConnected || e !== this.generation)) return;
274
+ let d = this.compose(u, n), f = this.ownerDocument.createElement("div");
275
+ if (f.className = "scripture-stage", f.setAttribute("aria-hidden", "true"), f.append(...d), this.root.append(f), yield* v.addFinalizer(() => v.sync(() => f.remove())), f.offsetHeight, yield* v.promise(() => this.ownerDocument.fonts.ready).pipe(v.timeoutOrElse({
276
+ duration: "3 seconds",
277
+ orElse: () => v.void
278
+ })), !this.isConnected || e !== this.generation) return;
279
+ let p = this.offsetHeight;
280
+ this.root.replaceChildren(...d), this.loaded = {
281
+ display: u,
282
+ passageId: n
283
+ }, this.applyTheme(), i && this.growFrom(p), this.removeAttribute("aria-busy"), this.dispatchEvent(new CustomEvent("scripture-load", { detail: {
284
+ provider: a.id,
285
+ bibleVersionId: l,
286
+ passageId: n,
287
+ bibleVersion: u.version,
288
+ ...u.yvpBibleVersion ? {
289
+ yvpBibleVersionId: Number(l),
290
+ yvpBibleVersion: u.yvpBibleVersion
291
+ } : {}
292
+ } }));
293
+ });
294
+ installStylesheets(e) {
295
+ return v.forEach(e.stylesheets ?? [], ({ kind: e, href: t }) => v.suspend(() => {
296
+ let n = e === "font" ? this.ownerDocument.head : this.shadowRoot, r = n.querySelectorAll("link[rel=\"stylesheet\"]"), i = Array.from(r).find((e) => e.href === t);
297
+ if (!i) i = this.ownerDocument.createElement("link"), i.rel = "stylesheet", i.href = t, n.append(i);
298
+ else if (i.sheet || i.dataset.scriptureLoaded) return v.void;
299
+ let a = i;
300
+ return C(a, ["load", "error"]).pipe(v.tap(() => v.sync(() => {
301
+ a.dataset.scriptureLoaded = "";
302
+ })), v.timeoutOrElse({
303
+ duration: "5 seconds",
304
+ orElse: () => v.void
305
+ }));
306
+ }), {
307
+ concurrency: "unbounded",
308
+ discard: !0
309
+ });
310
+ }
311
+ growFrom(e) {
312
+ let t = this.offsetHeight, n = "cubic-bezier(0.23, 1, 0.32, 1)", r = Array.from(this.root.children), i = this;
313
+ this.cancelAnimation = v.runCallback(v.gen(function* () {
314
+ let a = yield* v.acquireRelease(v.sync(() => {
315
+ if (A.matches) return r.map((e) => e.animate({ opacity: [0, 1] }, {
316
+ duration: 200,
317
+ easing: n
318
+ }));
319
+ let a = [];
320
+ return e !== t && (i.style.overflow = "hidden", a.push(i.animate({ height: [`${e}px`, `${t}px`] }, {
321
+ duration: 280,
322
+ easing: n
323
+ }))), a.push(...r.map((e) => e.animate({
324
+ opacity: [0, 1],
325
+ filter: ["blur(4px)", "blur(0)"]
326
+ }, {
327
+ duration: 280,
328
+ easing: n
329
+ }))), a;
330
+ }), (e) => v.sync(() => {
331
+ for (let t of e) t.cancel();
332
+ i.style.overflow = "";
333
+ }));
334
+ yield* v.forEach(a, (e) => v.tryPromise(() => e.finished).pipe(v.ignore), {
335
+ concurrency: "unbounded",
336
+ discard: !0
337
+ });
338
+ }).pipe(v.scoped));
339
+ }
340
+ };
341
+ function N(e = {}) {
342
+ Object.assign(s, e), customElements.get("si-passage") || customElements.define(w, M), customElements.get("bible-sk-passage") || customElements.define(T, class extends M {});
343
+ }
344
+ // src/card.css?inline
345
+ var P = ":host{all:initial;--scripture-font-size:17px;--scripture-line-height:1.6;pointer-events:none;z-index:2147483000;position:fixed;inset:0}.card{--bg:#fff;--fg:#1c1917;--muted:#78716c;--border:#1c19171a;--accent:#b91c1c;--hover:#f0eeed;--yv-font-sans:ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", sans-serif;box-sizing:border-box;width:min(380px,100vw - 16px);max-height:min(440px, 70vh, var(--space,100vh));pointer-events:auto;background:var(--bg);color:var(--fg);border:1px solid var(--border);font:14px/1.4 var(--yv-font-sans);--ease-out:cubic-bezier(.23, 1, .32, 1);opacity:0;transform:translateY(var(--enter-shift,4px)) scale(.97);transform-origin:var(--origin-x,50%) var(--origin-y,100%);transition:opacity .18s var(--ease-out), transform .18s var(--ease-out);border-radius:14px;outline:none;flex-direction:column;display:flex;position:absolute;box-shadow:0 1px 2px #0000000f,0 12px 32px -8px #00000038}.card[data-state=open]{opacity:1;transform:none}.card[data-state=closing]{transition-duration:.14s}@media (prefers-color-scheme:dark){.card{--bg:#1f1d1c;--fg:#f5f5f4;--muted:#a8a29e;--border:#ffffff1a;--accent:#f87171;--hover:#2c2a29}}@media (prefers-reduced-motion:reduce){.card{transition:opacity .12s linear;transform:none}}.close{all:unset;z-index:1;width:28px;height:28px;color:var(--muted);cursor:pointer;transition:background-color .15s ease, color .15s ease, transform .16s var(--ease-out);border-radius:8px;place-items:center;display:grid;position:absolute;top:12px;right:12px}@media (hover:hover) and (pointer:fine){.close:hover{background:var(--hover);color:var(--fg)}}.close:active{transform:scale(.95)}.close:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.body{overscroll-behavior:contain;padding:16px;overflow-y:auto}.body>:not([reference=citation],[reference=quote],[reference=none])::part(citation){padding-inline-end:36px}.body>:is([reference=citation],[reference=none])::part(text),.body>[reference=quote]::part(quote),.body>*::part(error){margin-block-start:28px}", F = 8, I = 8;
346
+ function L({ reference: e, ...n }) {
347
+ N();
348
+ let r = t(n), i = n.bibleVersionId || (r.id === "youversion" && n.yvpBibleVersionId ? String(n.yvpBibleVersionId) : r.defaultVersionId), a = document.createElement("scripture-card"), o = a.attachShadow({ mode: "open" }), s = document.createElement("style");
349
+ s.textContent = P, o.append(s);
350
+ let c = null, l = null, u = !1, f, p, h = b.makeUnsafe();
351
+ function g() {
352
+ if (!c || !l) return;
353
+ let e = l.getBoundingClientRect();
354
+ c.style.removeProperty("--space");
355
+ let t = c.offsetWidth, n = parseFloat(getComputedStyle(c).maxHeight), r = e.top - F - I, i = innerHeight - e.bottom - F - I, a = n <= r || n > i && r > i;
356
+ c.style.setProperty("--space", `${Math.max(a ? r : i, 120)}px`);
357
+ let o = Math.min(Math.max(I, e.left + e.width / 2 - t / 2), innerWidth - I - t);
358
+ c.style.top = a ? "" : `${e.bottom + F}px`, c.style.bottom = a ? `${innerHeight - e.top + F}px` : "", c.style.left = `${o}px`, c.style.setProperty("--origin-x", `${e.left + e.width / 2 - o}px`), c.style.setProperty("--origin-y", a ? "100%" : "0%"), c.style.setProperty("--enter-shift", a ? "4px" : "-4px");
359
+ }
360
+ function _(e = !1) {
361
+ f?.();
362
+ let t = c;
363
+ if (!t) return;
364
+ let n = l;
365
+ c = null, l = null, A(), e ? t.remove() : (t.dataset.state = "closing", v.runSync(C(t, ["transitionend"]).pipe(v.timeoutOrElse({
366
+ duration: "250 millis",
367
+ orElse: () => v.void
368
+ }), v.ensuring(v.sync(() => t.remove())), v.forkIn(h))), n && o.activeElement && n.focus());
369
+ }
370
+ function T(t, n = !1) {
371
+ let s = c !== null;
372
+ _(s), u = n;
373
+ let p = t.dataset.passage, y = d(m(p));
374
+ a.isConnected || document.body.append(a), c = document.createElement("div"), c.className = "card", s && (c.dataset.state = "open"), c.tabIndex = -1, c.setAttribute("role", "dialog"), c.setAttribute("aria-label", y), c.innerHTML = "\n <button class=\"close\" type=\"button\" aria-label=\"Close\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" aria-hidden=\"true\">\n <path d=\"M3 3l8 8M11 3l-8 8\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" />\n </svg>\n </button>\n <div class=\"body\" aria-live=\"polite\"></div>";
375
+ let b = document.createElement(w);
376
+ if (b.bibleProvider = r, b.setAttribute("bible-version-id", i), b.setAttribute("passage-id", p), b.setAttribute("theme", "auto"), b.setAttribute("reference", e), b.addEventListener("scripture-error", (e) => console.error("[scripture-inline]", e.detail)), c.querySelector(".body").append(b), c.querySelector(".close").addEventListener("click", () => _()), c.addEventListener("pointerenter", () => f?.()), c.addEventListener("pointerleave", M), o.append(c), l = t, g(), k(), n || c.focus({ preventScroll: !0 }), !s) {
377
+ c.getBoundingClientRect();
378
+ let e = c;
379
+ v.runSync(S.pipe(v.andThen(v.sync(() => {
380
+ c === e && (e.dataset.state = "open");
381
+ })), v.forkIn(h)));
382
+ }
383
+ }
384
+ let E = (e) => e.key === "Escape" && _(), D = (e) => {
385
+ let t = e.composedPath();
386
+ c && !t.includes(c) && !(l && t.includes(l)) && _();
387
+ }, O = () => v.runSync(S.pipe(v.andThen(v.sync(g)), v.forkIn(h)));
388
+ function k() {
389
+ p = v.runCallback(v.gen(function* () {
390
+ yield* x(document, "keydown", E), yield* x(document, "pointerdown", D, !0), yield* x(window, "scroll", O, {
391
+ capture: !0,
392
+ passive: !0
393
+ }), yield* x(window, "resize", O, { passive: !0 }), yield* v.never;
394
+ }).pipe(v.scoped));
395
+ }
396
+ function A() {
397
+ p?.();
398
+ }
399
+ function j(e) {
400
+ f?.(), l !== e && (!c || u) && (f = v.runCallback(v.sleep(c ? 150 : 350).pipe(v.andThen(v.sync(() => T(e, !0))), v.withSpan("scripture.card.hover"))));
401
+ }
402
+ function M(e) {
403
+ e && e.pointerType !== "mouse" || (f?.(), c && u && (f = v.runCallback(v.sleep(250).pipe(v.andThen(v.sync(() => _())), v.withSpan("scripture.card.unhover")))));
404
+ }
405
+ return {
406
+ toggle(e) {
407
+ l === e && u ? (f?.(), u = !1, c.focus({ preventScroll: !0 })) : l === e ? _() : T(e);
408
+ },
409
+ hover: j,
410
+ unhover: M,
411
+ close: _,
412
+ closeIfDetached() {
413
+ l && !l.isConnected && _(!0);
414
+ },
415
+ destroy() {
416
+ _(!0), v.runSync(b.close(h, y.void)), a.remove();
417
+ }
418
+ };
419
+ }
420
+ // src/find.ts
421
+ var ee = /(?<![\w:.])(?:[1-3]\s?)?[A-Za-z]{2,}\.?(?:\s+of\s+[A-Za-z]+)?\s+\d{1,3}(?:[:.]\d{1,3})?(?:\s?[-\u2010-\u2015]\s?\d{1,3}(?:[:.]\d{1,3})?)?(?![\w:])/g;
422
+ function R(e) {
423
+ let t = [], n = new RegExp(ee), r;
424
+ for (; r = n.exec(e);) {
425
+ let e = _(r[0]);
426
+ e.ok ? t.push({
427
+ start: r.index,
428
+ end: r.index + r[0].length,
429
+ text: r[0],
430
+ passage: e.value
431
+ }) : n.lastIndex = r.index + 1;
432
+ }
433
+ return t;
434
+ }
435
+ // src/scan.ts
436
+ var z = "scripture-inline-ref", B = "a,button,script,style,noscript,template,textarea,input,select,option,code,pre,kbd,samp,svg,math,[contenteditable],si-passage,si-passage-input,bible-sk-passage,bible-sk-passage-input,[data-scripture-inline-ignore]";
437
+ function V(e) {
438
+ let t = e.ownerDocument ?? e, n = t.createTreeWalker(e, NodeFilter.SHOW_TEXT, { acceptNode: (e) => e.parentElement && !e.parentElement.closest(B) && /\d/.test(e.nodeValue ?? "") ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT }), r = [];
439
+ for (; n.nextNode();) r.push(n.currentNode);
440
+ let i = [];
441
+ for (let e of r) {
442
+ let n = e.nodeValue ?? "", r = R(n);
443
+ if (r.length === 0) continue;
444
+ let a = t.createDocumentFragment(), o = 0;
445
+ for (let e of r) {
446
+ a.append(n.slice(o, e.start));
447
+ let r = t.createElement("a");
448
+ r.className = z, r.href = h("https://route.bible", e.passage.canonical), r.target = "_blank", r.rel = "noopener", r.dataset.passage = e.passage.canonical, r.textContent = e.text, a.append(r), i.push(r), o = e.end;
449
+ }
450
+ a.append(n.slice(o)), e.replaceWith(a);
451
+ }
452
+ return i;
453
+ }
454
+ function H(e) {
455
+ for (let t of e) {
456
+ let e = t.parentNode;
457
+ e && (t.replaceWith(t.textContent ?? ""), e.normalize());
458
+ }
459
+ }
460
+ // src/passage-input.ts
461
+ var U = "si-passage-input", W = "bible-sk-passage-input", te = "\n:host { display: block; position: relative; }\n.root { all: initial; display: block; position: relative; color: inherit; font: 0.875rem/1.5 var(--font-sans, system-ui, sans-serif); }\ninput {\n box-sizing: border-box;\n width: 100%;\n height: 2.25rem;\n margin: 0;\n padding: 0.25rem 0.75rem;\n font: inherit;\n color: inherit;\n background: none;\n border: 1px solid var(--input, color-mix(in srgb, currentColor 20%, transparent));\n border-radius: calc(var(--radius, 0.625rem) - 2px);\n box-shadow: 0 1px 2px #0000000d;\n outline: none;\n transition: color 0.15s, box-shadow 0.15s;\n}\n/* iOS Safari zooms the page into a focused field whose text is under 16px. */\n@media (pointer: coarse) { input { font-size: 16px; } }\ninput:focus-visible {\n border-color: var(--ring, Highlight);\n box-shadow: 0 0 0 3px color-mix(in oklab, var(--ring, Highlight) 50%, transparent);\n}\ninput[aria-invalid=\"true\"] { border-color: var(--destructive, #dc2626); }\ninput:disabled { cursor: not-allowed; opacity: 0.5; }\n[role=\"listbox\"] {\n position: absolute;\n inset: calc(100% + 4px) 0 auto;\n z-index: 50;\n box-sizing: border-box;\n max-height: 16rem;\n margin: 0;\n padding: 4px;\n overflow-y: auto;\n list-style: none;\n color: var(--popover-foreground, inherit);\n background: var(--popover, Canvas);\n border: 1px solid var(--border, color-mix(in srgb, currentColor 15%, transparent));\n border-radius: var(--radius, 0.625rem);\n box-shadow: 0 8px 24px -8px rgb(0 0 0 / 0.2);\n}\n[role=\"listbox\"][hidden] { display: none; }\n[role=\"option\"] > .si-passage-count {\n /* The label wraps before the count does. */\n flex-shrink: 0;\n white-space: nowrap;\n margin-inline-start: auto;\n font-size: 0.75rem;\n color: var(--muted-foreground, color-mix(in srgb, currentColor 60%, transparent));\n}\n[role=\"option\"] {\n /* Baseline alignment keeps the smaller count on the label's line. */\n display: flex;\n align-items: baseline;\n gap: 1rem;\n padding: 0.375rem 0.5rem;\n border-radius: calc(var(--radius, 0.625rem) - 4px);\n cursor: default;\n}\n[role=\"option\"][aria-selected=\"true\"] {\n color: var(--accent-foreground, inherit);\n background: var(--accent, color-mix(in srgb, currentColor 10%, transparent));\n}\n[role=\"option\"][aria-disabled=\"true\"] {\n color: var(--muted-foreground, color-mix(in srgb, currentColor 60%, transparent));\n}\n@media (hover: hover) and (pointer: fine) {\n [role=\"option\"]:not([aria-disabled]):hover {\n background: var(--accent, color-mix(in srgb, currentColor 10%, transparent));\n }\n}", ne = 0, G = (e, t) => `${e} ${t}${e === 1 ? "" : "s"}`, re = [...c].sort((e, t) => l[e].localeCompare(l[t], void 0, { numeric: !0 }));
462
+ function K(e) {
463
+ return (e === "alphabetical" ? re : c).map((e) => ({
464
+ kind: "book",
465
+ canonical: e,
466
+ label: l[e],
467
+ insertText: l[e]
468
+ }));
469
+ }
470
+ function q(e) {
471
+ return /^\s*(\d\s*)?[^\d:;,\-–—]+$/.test(e) ? g(e) : null;
472
+ }
473
+ function ie(e, t, n) {
474
+ let r = p(e, t) ?? 0;
475
+ return Array.from({ length: Math.max(r - n, 0) }, (r, i) => {
476
+ let a = `${l[e]} ${t}:${n}-${n + i + 1}`;
477
+ return {
478
+ kind: "range",
479
+ canonical: `${e}.${t}.${n}-${n + i + 1}`,
480
+ label: a,
481
+ insertText: a
482
+ };
483
+ });
484
+ }
485
+ var ae = (e) => e.replace(/^(\d) /, "$1\xA0").replace(/ (?=[\d:-]+$)/, "\xA0");
486
+ function oe(e, t) {
487
+ return Array.from({ length: p(e, t) ?? 0 }, (n, r) => {
488
+ let i = `${l[e]} ${t}:${r + 1}`;
489
+ return {
490
+ kind: "verse",
491
+ canonical: `${e}.${t}.${r + 1}`,
492
+ label: i,
493
+ insertText: i
494
+ };
495
+ });
496
+ }
497
+ function J(e) {
498
+ return Array.from({ length: f(e) }, (t, n) => {
499
+ let r = `${l[e]} ${n + 1}`;
500
+ return {
501
+ kind: "chapter",
502
+ canonical: `${e}.${n + 1}`,
503
+ label: r,
504
+ insertText: r
505
+ };
506
+ });
507
+ }
508
+ function se({ kind: e, canonical: t }) {
509
+ let [n, r] = t.split(".");
510
+ if (e === "book") return G(f(n), "chapter");
511
+ if (e === "chapter") {
512
+ let e = p(n, Number(r));
513
+ return e ? G(e, "verse") : "";
514
+ }
515
+ return "";
516
+ }
517
+ var Y = class extends HTMLElement {
518
+ static formAssociated = !0;
519
+ static observedAttributes = [
520
+ "value",
521
+ "placeholder",
522
+ "disabled",
523
+ "book-order"
524
+ ];
525
+ internals = this.attachInternals();
526
+ input = document.createElement("input");
527
+ list = document.createElement("ul");
528
+ suggestions = [];
529
+ active = -1;
530
+ committed = "";
531
+ constructor() {
532
+ super();
533
+ let e = document.createElement("div");
534
+ e.className = "root";
535
+ let t = document.createElement("style");
536
+ t.textContent = te, e.append(this.input, this.list), this.attachShadow({ mode: "open" }).append(t, e), this.input.setAttribute("part", "input"), this.list.setAttribute("part", "listbox");
537
+ let n = `${U}-${++ne}`;
538
+ Object.assign(this.input, {
539
+ type: "text",
540
+ autocomplete: "off",
541
+ spellcheck: !1
542
+ }), this.input.setAttribute("role", "combobox"), this.input.setAttribute("aria-autocomplete", "list"), this.input.setAttribute("aria-expanded", "false"), this.input.setAttribute("aria-controls", `${n}-list`), this.list.id = `${n}-list`, this.list.setAttribute("role", "listbox"), this.list.hidden = !0, this.input.addEventListener("input", () => {
543
+ this.input.removeAttribute("aria-invalid"), this.internals.setValidity({}), this.suggest();
544
+ }), this.input.addEventListener("keydown", (e) => this.onKeydown(e)), this.input.addEventListener("focus", () => this.suggest()), this.input.addEventListener("click", () => this.list.hidden && this.suggest()), this.input.addEventListener("change", (e) => e.stopPropagation()), this.input.addEventListener("blur", () => {
545
+ this.close(), this.commit(this.input.value);
546
+ }), this.list.addEventListener("pointerdown", (e) => e.preventDefault());
547
+ let r = (e) => {
548
+ let t = e.target.closest("[role=option]:not([aria-disabled])");
549
+ t && this.accept(this.suggestions[Number(t.dataset.index)]);
550
+ };
551
+ this.list.addEventListener("click", r);
552
+ let i;
553
+ this.list.addEventListener("touchstart", (e) => i = e.touches[0], { passive: !0 }), this.list.addEventListener("touchend", (e) => {
554
+ let t = e.changedTouches[0];
555
+ !i || !t || Math.hypot(t.clientX - i.clientX, t.clientY - i.clientY) > 10 || (e.preventDefault(), r(e));
556
+ }), this.addEventListener("click", (e) => e.composedPath()[0] === this && this.input.focus());
557
+ }
558
+ connectedCallback() {
559
+ let e = this.getAttribute("aria-label") ?? [...this.internals.labels].map((e) => e.textContent?.trim()).join(" ");
560
+ e && this.input.setAttribute("aria-label", e);
561
+ }
562
+ attributeChangedCallback(e, t, n) {
563
+ e === "value" ? this.commit(n ?? "", !1) : e === "placeholder" ? this.input.placeholder = n ?? "" : e === "disabled" ? this.input.disabled = n !== null : this.list.hidden || this.suggest();
564
+ }
565
+ get value() {
566
+ return this.committed;
567
+ }
568
+ set value(e) {
569
+ this.commit(e, !1);
570
+ }
571
+ get form() {
572
+ return this.internals.form;
573
+ }
574
+ get validity() {
575
+ return this.internals.validity;
576
+ }
577
+ get validationMessage() {
578
+ return this.internals.validationMessage;
579
+ }
580
+ checkValidity() {
581
+ return this.internals.checkValidity();
582
+ }
583
+ reportValidity() {
584
+ return this.internals.reportValidity();
585
+ }
586
+ focus(e) {
587
+ this.input.focus(e);
588
+ }
589
+ formResetCallback() {
590
+ this.commit(this.getAttribute("value") ?? "", !1);
591
+ }
592
+ formDisabledCallback(e) {
593
+ this.input.disabled = e;
594
+ }
595
+ commit(e, t = !0) {
596
+ let n = e.trim(), r = n ? _(n) : null, i = r?.ok && (r.value.start.book !== r.value.end.book || r.value.start.chapter !== r.value.end.chapter);
597
+ if (r && !r.ok || i) {
598
+ let t = i ? "Choose verses within one chapter." : "Enter a Bible passage, such as Romans 8.";
599
+ this.input.value = e, this.input.setAttribute("aria-invalid", "true"), this.internals.setValidity({ customError: !0 }, t, this.input);
600
+ return;
601
+ }
602
+ this.input.removeAttribute("aria-invalid"), this.internals.setValidity({});
603
+ let a = r?.value.canonical ?? "", o = r ? d(r.value) : "";
604
+ this.input.value = o, a !== this.committed && (this.committed = a, this.internals.setFormValue(a || null), t && this.dispatchEvent(new CustomEvent("change", {
605
+ bubbles: !0,
606
+ detail: {
607
+ passageId: a,
608
+ label: o
609
+ }
610
+ })));
611
+ }
612
+ get bookOrder() {
613
+ return this.getAttribute("book-order") === "alphabetical" ? "alphabetical" : "canonical";
614
+ }
615
+ options(e) {
616
+ if (!e) return K(this.bookOrder);
617
+ let t = /^(.+?)\s*(\d+)\s*:\s*(\d+)\s*[-–—]\s*(\d*)$/.exec(e), n = t && q(t[1]);
618
+ if (n) {
619
+ let e = t[4];
620
+ return ie(n, Number(t[2]), Number(t[3])).filter((t) => t.canonical.split("-")[1].startsWith(e));
621
+ }
622
+ let r = /^(.+?)\s*(\d+)\s*:\s*(\d*)$/.exec(e), i = r && q(r[1]);
623
+ if (i) return oe(i, Number(r[2])).filter((e) => e.canonical.split(".")[2].startsWith(r[3]));
624
+ let a = /^(.+?)\s*(\d+)$/.exec(e), o = a && q(a[1]);
625
+ if (o) return J(o).filter((e) => e.canonical.split(".")[1].startsWith(a[2]));
626
+ let s = u(e, { limit: 200 }).filter((e) => e.kind === "book"), c = K(this.bookOrder).map((e) => e.canonical);
627
+ s.sort((e, t) => c.indexOf(e.canonical) - c.indexOf(t.canonical));
628
+ let l = q(e), f = s.find((t) => t.insertText.toLowerCase() === e.toLowerCase());
629
+ if (l && (f || !s.length)) return J(l);
630
+ if (s.length) return s;
631
+ let p = _(e);
632
+ if (!p.ok) return [];
633
+ let { start: m, end: h, canonical: g, rangeType: v } = p.value;
634
+ if (m.book !== h.book || m.chapter !== h.chapter) return [];
635
+ let y = d(p.value);
636
+ return [{
637
+ kind: v === "chapter" ? "chapter" : v === "single" ? "verse" : "range",
638
+ canonical: g,
639
+ label: y,
640
+ insertText: y
641
+ }];
642
+ }
643
+ suggest() {
644
+ if (this.suggestions = this.options(this.input.value.trim()), this.active = -1, this.list.replaceChildren(...this.suggestions.map((e, t) => {
645
+ let n = document.createElement("li");
646
+ n.id = `${this.list.id}-${t}`, n.dataset.index = String(t), n.setAttribute("role", "option"), n.setAttribute("part", "option"), n.setAttribute("aria-selected", "false"), n.textContent = ae(e.label);
647
+ let r = se(e);
648
+ if (r) {
649
+ let e = document.createElement("span");
650
+ e.className = "si-passage-count", e.setAttribute("part", "count"), e.textContent = r, n.append(e);
651
+ }
652
+ return n;
653
+ })), !this.suggestions.length && this.input.value.trim()) {
654
+ let e = document.createElement("li");
655
+ e.setAttribute("role", "option"), e.setAttribute("part", "option"), e.setAttribute("aria-disabled", "true"), e.textContent = "No passage matches", this.list.append(e);
656
+ }
657
+ this.list.hidden = !this.list.childElementCount, this.input.setAttribute("aria-expanded", String(!this.list.hidden)), this.input.removeAttribute("aria-activedescendant");
658
+ }
659
+ highlight(e) {
660
+ this.active = e;
661
+ for (let t of this.list.children) t.setAttribute("aria-selected", String(t.id === `${this.list.id}-${e}`));
662
+ let t = this.list.children[e];
663
+ t ? (this.input.setAttribute("aria-activedescendant", t.id), t.scrollIntoView({ block: "nearest" })) : this.input.removeAttribute("aria-activedescendant");
664
+ }
665
+ close() {
666
+ this.list.hidden = !0, this.input.setAttribute("aria-expanded", "false"), this.input.removeAttribute("aria-activedescendant"), this.active = -1;
667
+ }
668
+ accept(e) {
669
+ e.kind === "book" ? (this.input.value = `${e.insertText} `, this.suggest()) : (this.close(), this.commit(e.insertText));
670
+ }
671
+ onKeydown(e) {
672
+ let t = !this.list.hidden, n = this.suggestions.length;
673
+ switch (e.key) {
674
+ case "ArrowDown":
675
+ case "ArrowUp": {
676
+ if (e.preventDefault(), !t) return this.suggest();
677
+ let r = this.active + (e.key === "ArrowDown" ? 1 : -1);
678
+ this.highlight(r >= n ? -1 : r < -1 ? n - 1 : r);
679
+ return;
680
+ }
681
+ case "Enter": {
682
+ let r = _(this.input.value.trim());
683
+ return t && e.preventDefault(), !this.input.value.trim() && this.active < 0 ? (this.close(), this.commit("")) : this.active >= 0 ? this.accept(this.suggestions[this.active]) : r.ok || !n ? (this.close(), this.commit(this.input.value)) : this.accept(this.suggestions[0]);
684
+ }
685
+ case "Tab":
686
+ if (n && !e.shiftKey && this.input.value.trim()) {
687
+ let t = this.suggestions[Math.max(this.active, 0)];
688
+ t.kind === "book" && `${t.insertText} ` !== this.input.value && (e.preventDefault(), this.accept(t));
689
+ }
690
+ return;
691
+ case "Escape":
692
+ t ? (e.preventDefault(), this.close()) : this.commit(this.committed, !1);
693
+ return;
694
+ }
695
+ }
696
+ };
697
+ function ce() {
698
+ customElements.get("si-passage-input") || customElements.define(U, Y), customElements.get("bible-sk-passage-input") || customElements.define(W, class extends Y {});
699
+ }
700
+ // src/version-input.ts
701
+ var X = "si-version-input", Z = "bible-sk-version-input", le = "\n:host { display: block; position: relative; min-width: 0; }\n.root { all: initial; display: grid; gap: 0.375rem; color: inherit; font: 0.875rem/1.5 var(--font-sans, system-ui, sans-serif); }\ninput, select, button { box-sizing: border-box; font: inherit; color: inherit; }\ninput, select {\n width: 100%; min-width: 0; height: 2.25rem; margin: 0; padding: 0.25rem 0.75rem;\n background: none; border: 1px solid var(--input, color-mix(in srgb, currentColor 20%, transparent));\n border-radius: calc(var(--radius, 0.625rem) - 2px); outline: none; box-shadow: 0 1px 2px #0000000d;\n transition: color 0.15s, box-shadow 0.15s;\n}\ninput:focus-visible, select:focus-visible {\n border-color: var(--ring, Highlight);\n box-shadow: 0 0 0 3px color-mix(in oklab, var(--ring, Highlight) 50%, transparent);\n}\nbutton:focus-visible { outline: 2px solid var(--ring, Highlight); outline-offset: 2px; }\ninput:disabled, select:disabled { opacity: 0.5; cursor: not-allowed; }\ninput[aria-invalid=\"true\"] { border-color: var(--destructive, #dc2626); }\n.language { position: relative; }\n.language select { appearance: none; padding-inline-end: 2.5rem; }\n.language svg { position: absolute; inset-inline-end: 0.75rem; top: 50%; transform: translateY(-50%); pointer-events: none; }\n.language select:disabled + svg { opacity: 0.5; }\n.search { position: relative; }\n[role=\"listbox\"] {\n position: absolute; inset: calc(100% + 4px) 0 auto; z-index: 50; box-sizing: border-box;\n max-height: 16rem; margin: 0; padding: 4px; overflow-y: auto; list-style: none;\n color: var(--popover-foreground, inherit); background: var(--popover, Canvas);\n border: 1px solid var(--border, color-mix(in srgb, currentColor 15%, transparent));\n border-radius: var(--radius, 0.625rem); box-shadow: 0 8px 24px -8px #0003;\n}\n[hidden] { display: none !important; }\n[role=\"option\"] { padding: 0.5rem; border-radius: calc(var(--radius, 0.625rem) - 4px); overflow-wrap: anywhere; cursor: default; }\n[role=\"option\"][aria-selected=\"true\"] { color: var(--accent-foreground, inherit); background: var(--accent, color-mix(in srgb, currentColor 10%, transparent)); }\n.language-name { display: block; font-size: 0.75rem; opacity: 0.7; }\n.feedback { display: flex; align-items: center; gap: 0.5rem; min-height: 1.5rem; font-size: 0.75rem; }\n[role=\"status\"] { display: flex; align-items: center; gap: 0.375rem; }\nsvg { flex: none; }\n[role=\"status\"] svg { animation: spin 0.8s linear infinite; }\nbutton { border: 0; padding: 0; background: none; text-decoration: underline; cursor: pointer; }\n@keyframes spin { to { transform: rotate(360deg); } }\n@media (pointer: coarse) { input, select { font-size: 16px; } }\n@media (prefers-reduced-motion: reduce) { [role=\"status\"] svg { animation-duration: 2.4s; } }\n@media (hover: hover) and (pointer: fine) { [role=\"option\"]:hover { background: var(--accent, color-mix(in srgb, currentColor 10%, transparent)); } }\n", ue = 0, Q = (e) => `${e.title} (${e.abbreviation})`, $ = class extends HTMLElement {
702
+ static formAssociated = !0;
703
+ static observedAttributes = [
704
+ "value",
705
+ "language",
706
+ "provider",
707
+ "yvp-public-app-key",
708
+ "yvp-api-host",
709
+ "placeholder",
710
+ "disabled"
711
+ ];
712
+ internals = this.attachInternals();
713
+ input = document.createElement("input");
714
+ language = document.createElement("select");
715
+ list = document.createElement("ul");
716
+ status = document.createElement("span");
717
+ retry = document.createElement("button");
718
+ versions = [];
719
+ filtered = [];
720
+ selected;
721
+ pendingValue;
722
+ provider;
723
+ override;
724
+ active = -1;
725
+ generation = 0;
726
+ scheduled = !1;
727
+ cancelLoad;
728
+ loading = !1;
729
+ failed = !1;
730
+ formDisabled = !1;
731
+ constructor() {
732
+ super();
733
+ let e = document.createElement("style");
734
+ e.textContent = le;
735
+ let t = document.createElement("div");
736
+ t.className = "root";
737
+ let n = document.createElement("div");
738
+ n.className = "language", n.append(this.language), n.insertAdjacentHTML("beforeend", "<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"m6 9 6 6 6-6\"/></svg>");
739
+ let r = document.createElement("div");
740
+ r.className = "search", r.append(this.input, this.list);
741
+ let i = document.createElement("div");
742
+ i.className = "feedback", i.append(this.status, this.retry), t.append(n, r, i), this.attachShadow({ mode: "open" }).append(e, t), this.input.type = "text", this.input.autocomplete = "off", this.input.spellcheck = !1, this.input.placeholder = "Search translations", this.input.setAttribute("part", "input"), this.input.setAttribute("role", "combobox"), this.input.setAttribute("aria-autocomplete", "list"), this.input.setAttribute("aria-expanded", "false"), this.list.id = `${X}-${++ue}`, this.list.setAttribute("part", "listbox"), this.list.setAttribute("role", "listbox"), this.list.setAttribute("aria-label", "Bible translations"), this.list.hidden = !0, this.input.setAttribute("aria-controls", this.list.id), this.language.setAttribute("part", "language"), this.language.setAttribute("aria-label", "Translation language"), this.status.setAttribute("role", "status"), this.status.setAttribute("part", "status"), this.retry.type = "button", this.retry.textContent = "Retry", this.retry.setAttribute("part", "retry"), this.retry.hidden = !0, this.retry.addEventListener("click", () => this.scheduleLoad()), this.input.addEventListener("input", () => this.suggest()), this.input.addEventListener("focus", () => {
743
+ this.input.select(), this.suggest();
744
+ }), this.input.addEventListener("click", () => this.list.hidden && this.suggest()), this.input.addEventListener("change", (e) => e.stopPropagation()), this.input.addEventListener("blur", () => {
745
+ this.input.value = this.selected ? Q(this.selected) : "", this.close();
746
+ }), this.language.addEventListener("change", (e) => {
747
+ e.stopPropagation(), this.input.focus(), this.suggest();
748
+ }), this.input.addEventListener("keydown", (e) => {
749
+ e.key === "Escape" ? (this.close(), this.input.value = this.selected ? Q(this.selected) : "") : e.key === "ArrowDown" || e.key === "ArrowUp" ? (e.preventDefault(), this.list.hidden ? this.suggest() : this.highlight(Math.max(0, Math.min(this.filtered.length - 1, this.active + (e.key === "ArrowDown" ? 1 : -1))))) : e.key === "Enter" && !this.list.hidden && (e.preventDefault(), this.filtered[this.active] && this.commit(this.filtered[this.active].id, !0));
750
+ }), this.list.addEventListener("pointerdown", (e) => e.preventDefault());
751
+ let a = (e) => {
752
+ let t = e.target.closest("[role=option]");
753
+ t && this.commit(this.filtered[Number(t.dataset.index)].id, !0);
754
+ };
755
+ this.list.addEventListener("click", a);
756
+ let o;
757
+ this.list.addEventListener("touchstart", (e) => {
758
+ o = e.touches[0];
759
+ }, { passive: !0 }), this.list.addEventListener("touchend", (e) => {
760
+ let t = e.changedTouches[0];
761
+ !o || !t || Math.hypot(t.clientX - o.clientX, t.clientY - o.clientY) > 10 || (e.preventDefault(), a(e));
762
+ }), this.addEventListener("click", (e) => e.composedPath()[0] === this && this.input.focus());
763
+ }
764
+ connectedCallback() {
765
+ this.input.setAttribute("aria-label", this.getAttribute("aria-label") || [...this.internals.labels].map((e) => e.textContent?.trim()).join(" ") || "Bible translation"), this.scheduleLoad();
766
+ }
767
+ disconnectedCallback() {
768
+ this.generation++, this.cancelLoad?.(), this.close();
769
+ }
770
+ attributeChangedCallback(e, t, n) {
771
+ t !== n && (e === "value" ? (this.pendingValue = n ?? "", this.loading || this.commit(this.pendingValue, !1)) : e === "placeholder" ? this.input.placeholder = n ?? "Search translations" : e === "disabled" ? this.updateDisabled() : e === "language" ? (this.language.value = n ?? "all", this.close()) : this.isConnected && this.scheduleLoad());
772
+ }
773
+ get value() {
774
+ return this.selected?.id ?? "";
775
+ }
776
+ set value(e) {
777
+ this.pendingValue = e, this.loading || this.commit(e, !1);
778
+ }
779
+ get bibleProvider() {
780
+ return this.override;
781
+ }
782
+ set bibleProvider(e) {
783
+ this.override = e, this.isConnected && this.scheduleLoad();
784
+ }
785
+ get form() {
786
+ return this.internals.form;
787
+ }
788
+ get validity() {
789
+ return this.internals.validity;
790
+ }
791
+ get validationMessage() {
792
+ return this.internals.validationMessage;
793
+ }
794
+ checkValidity() {
795
+ return this.internals.checkValidity();
796
+ }
797
+ reportValidity() {
798
+ return this.internals.reportValidity();
799
+ }
800
+ focus(e) {
801
+ this.input.focus(e);
802
+ }
803
+ formResetCallback() {
804
+ this.value = this.defaultValue(this.provider ?? t(s));
805
+ }
806
+ formDisabledCallback(e) {
807
+ this.formDisabled = e, this.updateDisabled();
808
+ }
809
+ defaultValue(e) {
810
+ let n = e === t(s) ? s.bibleVersionId ?? (e.id === "youversion" ? s.yvpBibleVersionId?.toString() : void 0) : void 0;
811
+ return this.getAttribute("value") ?? n ?? e.defaultVersionId;
812
+ }
813
+ updateDisabled() {
814
+ let e = this.loading || this.failed || this.formDisabled || this.hasAttribute("disabled");
815
+ this.input.disabled = e, this.language.disabled = e, this.retry.disabled = this.formDisabled || this.hasAttribute("disabled"), e && this.close();
816
+ }
817
+ close() {
818
+ this.list.hidden = !0, this.input.setAttribute("aria-expanded", "false"), this.input.removeAttribute("aria-activedescendant");
819
+ }
820
+ highlight(e) {
821
+ this.active = e, [...this.list.children].forEach((t, n) => t.setAttribute("aria-selected", String(n === e)));
822
+ let t = this.list.children[e];
823
+ t ? (this.input.setAttribute("aria-activedescendant", t.id), t.offsetTop < this.list.scrollTop ? this.list.scrollTop = t.offsetTop : t.offsetTop + t.offsetHeight > this.list.scrollTop + this.list.clientHeight && (this.list.scrollTop = t.offsetTop + t.offsetHeight - this.list.clientHeight)) : this.input.removeAttribute("aria-activedescendant");
824
+ }
825
+ suggest() {
826
+ if (this.input.disabled) return;
827
+ let e = this.selected && this.input.value === Q(this.selected) ? "" : this.input.value.trim().toLocaleLowerCase();
828
+ this.filtered = this.versions.filter((t) => (!this.language.value || this.language.value === "all" || t.language === this.language.value) && `${t.title} ${t.abbreviation} ${t.languageName ?? ""}`.toLocaleLowerCase().includes(e)), this.list.replaceChildren(...this.filtered.map((e, t) => {
829
+ let n = document.createElement("li");
830
+ if (n.id = `${this.list.id}-${t}`, n.dataset.index = String(t), n.setAttribute("role", "option"), n.setAttribute("part", "option"), n.textContent = Q(e), this.language.value === "all" && e.languageName) {
831
+ let t = document.createElement("span");
832
+ t.className = "language-name", t.textContent = e.languageName, n.append(t);
833
+ }
834
+ return n;
835
+ })), this.status.textContent = this.filtered.length ? `${this.filtered.length} translations` : "No matching translations. Try another search or language.", this.list.hidden = !this.filtered.length, this.input.setAttribute("aria-expanded", String(!this.list.hidden)), this.highlight(Math.max(0, this.filtered.findIndex((e) => e.id === this.value)));
836
+ }
837
+ commit(e, t) {
838
+ let n = this.value;
839
+ this.selected = this.versions.find((t) => t.id === e), this.input.value = this.selected ? Q(this.selected) : "", this.internals.setFormValue(this.selected?.id ?? null);
840
+ let r = !!(e && !this.selected && this.provider);
841
+ this.internals.setValidity(r ? { customError: !0 } : {}, r ? "This translation is not available from the selected provider." : "", this.input), this.input.setAttribute("aria-invalid", String(r)), r ? this.status.textContent = "This translation is not available from the selected provider." : this.status.textContent = `${this.versions.filter((e) => !this.language.value || this.language.value === "all" || e.language === this.language.value).length} translations available`, this.close(), t && n !== this.value && (this.pendingValue = this.value, this.dispatchEvent(new CustomEvent("change", {
842
+ bubbles: !0,
843
+ detail: {
844
+ provider: this.provider.id,
845
+ bibleVersionId: this.value,
846
+ bibleVersion: this.selected ?? null
847
+ }
848
+ })));
849
+ }
850
+ scheduleLoad() {
851
+ this.generation++, this.cancelLoad?.(), this.scheduled || (this.scheduled = !0, queueMicrotask(() => {
852
+ this.scheduled = !1, this.isConnected && (this.cancelLoad = v.runCallback(this.load(this.generation).pipe(v.catch((e) => v.sync(() => {
853
+ this.selected = void 0, this.versions = [], this.input.value = "", this.status.textContent = "Unable to load translations.", this.internals.setValidity({ customError: !0 }, "Unable to load translations.", this.input), this.input.setAttribute("aria-invalid", "true"), this.loading = !1, this.failed = !0, this.updateDisabled(), this.retry.hidden = !1, this.dispatchEvent(new CustomEvent("scripture-error", { detail: e }));
854
+ })))));
855
+ }));
856
+ }
857
+ load = v.fn("scripture.versionInput.load")(function* (e) {
858
+ let t = this.pendingValue ?? this.selected?.id;
859
+ this.loading = !0, this.failed = !1, this.updateDisabled(), this.retry.hidden = !0, this.status.innerHTML = "<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><path d=\"M21 12a9 9 0 1 1-6.219-8.56\"/></svg>Loading translations", this.internals.setFormValue(null), this.internals.setValidity({}), this.input.setAttribute("aria-invalid", "false");
860
+ let n = yield* r({
861
+ ...s,
862
+ provider: this.getAttribute("provider") || this.override || s.provider,
863
+ yvpPublicAppKey: this.getAttribute("yvp-public-app-key") || s.yvpPublicAppKey,
864
+ yvpApiHost: this.getAttribute("yvp-api-host") || s.yvpApiHost
865
+ }), i = yield* a({ provider: n });
866
+ if (!this.isConnected || e !== this.generation) return;
867
+ this.provider = n, this.versions = [...i].sort((e, t) => e.title.localeCompare(t.title));
868
+ let o = /* @__PURE__ */ new Map();
869
+ for (let e of i) e.language && o.set(e.language, e.languageName || e.language);
870
+ this.language.replaceChildren(new Option("All languages", "all"), ...[...o].sort((e, t) => e[1].localeCompare(t[1])).map(([e, t]) => new Option(t, e)));
871
+ let c = this.pendingValue ?? t ?? this.defaultValue(n), l = i.find((e) => e.id === c), u = this.getAttribute("language") || l?.language || new Intl.Locale(navigator.language).language;
872
+ this.language.value = o.has(u) ? u : "all", this.commit(c, !1), this.pendingValue = void 0, this.loading = !1, this.updateDisabled(), this.dispatchEvent(new CustomEvent("scripture-versions-load", { detail: {
873
+ provider: n.id,
874
+ versions: i
875
+ } }));
876
+ });
877
+ };
878
+ function de(e = {}) {
879
+ Object.assign(s, e), customElements.get("si-version-input") || customElements.define(X, $), customElements.get("bible-sk-version-input") || customElements.define(Z, class extends $ {});
880
+ }
881
+ // src/index.ts
882
+ var fe = `
883
+ .${z} {
884
+ /* Lay out exactly like the text it replaced, whatever the host styles links as,
885
+ so linking a reference never reflows the line. Only paint-only styles below. */
886
+ display: inline !important;
887
+ font: inherit !important;
888
+ letter-spacing: inherit !important;
889
+ word-spacing: inherit !important;
890
+ margin: 0 !important;
891
+ padding: 0 !important;
892
+ border: 0 !important;
893
+ color: inherit;
894
+ text-decoration: underline dotted;
895
+ text-decoration-thickness: 1.5px;
896
+ text-underline-offset: 0.2em;
897
+ cursor: pointer;
898
+ }
899
+ .${z}:hover { text-decoration-style: solid; }`;
900
+ function pe({ mode: e = "card", trigger: t = "click", reference: n = "heading", root: r, watch: i = !0, ...a } = {}) {
901
+ if (!document.getElementById("scripture-inline-styles")) {
902
+ let e = document.createElement("style");
903
+ e.id = "scripture-inline-styles", e.textContent = fe, document.head.append(e);
904
+ }
905
+ let o = V(r ?? document.body), s = new AbortController(), { signal: c } = s, l = null, u = r ?? document, d = new MutationObserver((e) => {
906
+ for (let t of e) for (let e of t.addedNodes) {
907
+ if (!e.isConnected || !r && !document.body?.contains(e)) continue;
908
+ let t = e.nodeType === Node.TEXT_NODE ? e.parentNode : e;
909
+ if (t && (o.push(...V(t)), t instanceof Element)) for (let e of t.querySelectorAll(`a.${z}`)) o.includes(e) || o.push(e);
910
+ }
911
+ d.takeRecords();
912
+ for (let e = o.length - 1; e >= 0; e--) o[e].isConnected || o.splice(e, 1);
913
+ l?.closeIfDetached();
914
+ });
915
+ if (e === "card") {
916
+ let e = l = L({
917
+ ...a,
918
+ reference: n
919
+ }), r = (e) => e.target.closest?.(`a.${z}`);
920
+ u.addEventListener("click", (t) => {
921
+ let n = t;
922
+ if (n.metaKey || n.ctrlKey || n.shiftKey || n.altKey || n.button !== 0) return;
923
+ let i = r(t);
924
+ i && (t.preventDefault(), e.toggle(i));
925
+ }, { signal: c }), t === "hover" && (u.addEventListener("pointerover", (t) => {
926
+ let n = r(t);
927
+ n && t.pointerType === "mouse" && e.hover(n);
928
+ }, { signal: c }), u.addEventListener("pointerout", (t) => {
929
+ r(t) && e.unhover(t);
930
+ }, { signal: c }));
931
+ }
932
+ return {
933
+ links: o,
934
+ stop: v.runCallback(v.gen(function* () {
935
+ yield* v.acquireRelease(v.sync(() => {
936
+ i && d.observe(r ?? document.documentElement, {
937
+ childList: !0,
938
+ subtree: !0
939
+ });
940
+ }), () => v.sync(() => {
941
+ d.disconnect(), s.abort(), l?.destroy(), H(o);
942
+ })), yield* v.never;
943
+ }).pipe(v.scoped, v.withSpan("scripture.references.watch")))
944
+ };
945
+ }
946
+ export { W as BIBLE_PASSAGE_INPUT_TAG, T as BIBLE_PASSAGE_TAG, Z as BIBLE_VERSION_INPUT_TAG, U as PASSAGE_INPUT_TAG, w as SCRIPTURE_PASSAGE_TAG, M as ScripturePassage, Y as ScripturePassageInput, $ as ScriptureVersionInput, X as VERSION_INPUT_TAG, L as createScriptureCard, i as createYouVersionProvider, ce as definePassageInput, N as defineScripturePassage, de as defineVersionInput, R as findReferences, n as helloaoProvider, e as listBibleVersions, pe as scriptureInline, H as unwrapReferences, V as wrapReferences };