daily-soup-widget 0.2.2 → 0.3.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/README.md CHANGED
@@ -51,7 +51,7 @@ export default function Page() {
51
51
 
52
52
  The component is SSR-safe: it emits a placeholder during render and hydrates the widget inside `useEffect`. No `window` access at module top-level.
53
53
 
54
- ## Install — framework-agnostic ESM
54
+ ## Install — imperative API (NPM)
55
55
 
56
56
  ```ts
57
57
  import { mount } from 'daily-soup-widget';
@@ -61,6 +61,9 @@ const handle = mount(host, { lang: 'zh', theme: 'auto' });
61
61
  // later: handle.destroy();
62
62
  ```
63
63
 
64
+ Same React peer requirement as the `<DailySoup>` component — install React + ReactDOM
65
+ (>=18) alongside.
66
+
64
67
  ---
65
68
 
66
69
  ## Configuration
@@ -130,10 +133,10 @@ scripts/
130
133
  build-schedule.ts content/*.md → dist/schedule-zh.json
131
134
  build-bundle.ts esbuild UMD + ESM + CJS bundles
132
135
  src/
133
- embed.ts UMD entry (CDN, auto-mounts)
136
+ embed.ts UMD entry (CDN, auto-mounts; React bundled in)
134
137
  index.ts ESM entry (NPM)
135
- component.tsx React wrapper
136
- widget.ts framework-agnostic core (Shadow DOM, render, fetch)
138
+ component.tsx React wrapper around mount()
139
+ widget.tsx React widget core (Shadow DOM root, render, fetch)
137
140
  theme.ts auto/light/dark resolution + system-theme listener
138
141
  i18n.ts UI strings (zh)
139
142
  share.ts copy / X / LINE share builders
@@ -165,6 +168,20 @@ npm run build # full prod build (schedule + bundle + types + Next.j
165
168
  2. `npm run build:schedule` — extends the schedule, preserving past dates.
166
169
  3. Commit + PR. Merge triggers the GH Action to redeploy.
167
170
 
171
+ ### Releasing a new npm version
172
+
173
+ 1. Bump `version` in `package.json`, commit, merge to `main`.
174
+ 2. Tag and push: `git tag v0.2.3 && git push --tags`.
175
+ 3. `.github/workflows/publish.yml` runs `npm test` + `typecheck` + `build:lib` + `npm publish` against the `NPM_TOKEN` repo secret. No local `npm login` ever needed.
176
+
177
+ The `NPM_TOKEN` must be a granular automation token scoped to read & write on `daily-soup-widget`.
178
+
179
+ **Verifying the auth chain without a version bump:** trigger `gh workflow run publish.yml -R mshmwr/daily-soup-widget` against the current `main`. The workflow runs all pre-publish steps and then attempts `npm publish` on the already-published version, which the registry rejects with `E403 You cannot publish over the previously published versions: <version>`. Reaching that exact error confirms the `NPM_TOKEN` secret is present and has publish scope — only the uniqueness check rejected. Use this whenever you rotate the token or change the workflow, instead of minting a throwaway version. Note: `npm whoami` on a granular automation token returns 401 and is not a valid auth-chain test; use `npm publish --dry-run` (locally) or this `workflow_dispatch` trick (in CI) instead.
180
+
181
+ ### Auditing source URLs
182
+
183
+ `npm run check:source-urls` HEADs every `sourceUrl` in `content/quotes/**` and flags pages whose body matches `資料已刪除|404 Not Found`. Run before each release; null any broken URLs in frontmatter (the widget renders source as plain text when `sourceUrl` is empty).
184
+
168
185
  ---
169
186
 
170
187
  ## Browser support
package/dist/embed.cjs.js CHANGED
@@ -26,6 +26,11 @@ __export(index_exports, {
26
26
  });
27
27
  module.exports = __toCommonJS(index_exports);
28
28
 
29
+ // src/widget.tsx
30
+ var import_react = require("react");
31
+ var import_client = require("react-dom/client");
32
+ var import_react_dom = require("react-dom");
33
+
29
34
  // src/i18n.ts
30
35
  var STRINGS = {
31
36
  zh: {
@@ -194,81 +199,9 @@ function todayUtc8(now = /* @__PURE__ */ new Date()) {
194
199
  return `${yy}-${mm}-${dd}`;
195
200
  }
196
201
 
197
- // src/widget.ts
202
+ // src/widget.tsx
203
+ var import_jsx_runtime = require("react/jsx-runtime");
198
204
  var DEFAULT_SCHEDULE_BASE = "https://daily-soup-widget.vercel.app";
199
- function escape(s) {
200
- return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
201
- }
202
- function attachRoot(host) {
203
- if (typeof host.attachShadow === "function") {
204
- if (host.shadowRoot) return host.shadowRoot;
205
- return host.attachShadow({ mode: "open" });
206
- }
207
- console.warn("[daily-soup] attachShadow unsupported, falling back to light DOM");
208
- return host;
209
- }
210
- function injectStyles(root) {
211
- const style = document.createElement("style");
212
- style.textContent = WIDGET_STYLES;
213
- root.appendChild(style);
214
- }
215
- function buildSkeleton(theme) {
216
- const card = document.createElement("div");
217
- card.className = `ds-card ds-${theme} ds-skeleton`;
218
- card.innerHTML = `
219
- <div class="ds-quote">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</div>
220
- <div class="ds-meta"><span class="ds-author">&nbsp;</span><span class="ds-source">&nbsp;</span></div>
221
- `;
222
- return card;
223
- }
224
- function renderQuote(card, quote, lang, theme) {
225
- const s = t(lang);
226
- card.className = `ds-card ds-${theme}`;
227
- const sourceLabel = quote.sourceUrl ? `<a href="${escape(quote.sourceUrl)}" target="_blank" rel="noopener noreferrer">${escape(quote.source)}</a>` : escape(quote.source);
228
- const flag = quote.attribution === "popular-attribution" ? `<span class="ds-flag">${escape(s.attributedPopular)}</span>` : "";
229
- card.innerHTML = `
230
- <p class="ds-quote">${escape(quote.text)}</p>
231
- <div class="ds-meta">
232
- <span class="ds-author">\u2014 ${escape(quote.author)}${flag}</span>
233
- ${quote.source ? `<span class="ds-source">${s.source}\uFF1A${sourceLabel}</span>` : ""}
234
- </div>
235
- <div class="ds-actions">
236
- <div class="ds-share">
237
- <button class="ds-btn" data-action="copy" type="button" aria-label="${escape(s.copy)}">
238
- <span aria-hidden="true">\u29C9</span><span class="ds-share-label">${escape(s.copy)}</span>
239
- </button>
240
- <a class="ds-btn" data-action="x" href="${escape(buildXShareUrl({ text: quote.text, author: quote.author }))}" target="_blank" rel="noopener noreferrer" aria-label="${escape(s.shareX)}">
241
- <span aria-hidden="true">\u{1D54F}</span><span class="ds-share-label">X</span>
242
- </a>
243
- <a class="ds-btn" data-action="line" href="${escape(buildLineShareUrl({ text: quote.text, author: quote.author }))}" target="_blank" rel="noopener noreferrer" aria-label="${escape(s.shareLine)}">
244
- <span aria-hidden="true">L</span><span class="ds-share-label">LINE</span>
245
- </a>
246
- </div>
247
- <span class="ds-powered"><a href="https://personal-site-mocha-chi.vercel.app" target="_blank" rel="noopener noreferrer">${s.poweredBy}</a></span>
248
- </div>
249
- `;
250
- const copyBtn = card.querySelector('[data-action="copy"]');
251
- if (copyBtn) {
252
- copyBtn.addEventListener("click", async () => {
253
- const ok = await copyToClipboard({ text: quote.text, author: quote.author });
254
- if (ok) {
255
- const label = copyBtn.querySelector(".ds-share-label");
256
- const originalLabel = label?.textContent ?? "";
257
- if (label) label.textContent = s.copied;
258
- copyBtn.classList.add("ds-toast");
259
- setTimeout(() => {
260
- if (label) label.textContent = originalLabel;
261
- copyBtn.classList.remove("ds-toast");
262
- }, 2e3);
263
- }
264
- });
265
- }
266
- }
267
- function renderError(card, lang, theme) {
268
- card.className = `ds-card ds-${theme}`;
269
- const s = t(lang);
270
- card.innerHTML = `<p class="ds-error">${escape(s.loadFailed)}</p>`;
271
- }
272
205
  async function fetchSchedule(scheduleUrl, lang) {
273
206
  const base = scheduleUrl.replace(/\/$/, "");
274
207
  const url = `${base}/schedule-${lang}.json`;
@@ -281,8 +214,7 @@ async function fetchSchedule(scheduleUrl, lang) {
281
214
  return null;
282
215
  }
283
216
  }
284
- function pickQuote(schedule) {
285
- const today = todayUtc8();
217
+ function pickQuote(schedule, today) {
286
218
  const id = schedule.entries[today];
287
219
  if (id && schedule.quotes[id]) return schedule.quotes[id];
288
220
  const fallbackId = Object.keys(schedule.quotes).sort()[0];
@@ -292,101 +224,231 @@ function pickQuote(schedule) {
292
224
  }
293
225
  return null;
294
226
  }
295
- function applyThemeOverrides(card, config, maxWidth) {
296
- const colors = getThemeColors(config);
227
+ function buildInlineStyle(themeConfig, maxWidth) {
228
+ const style = {};
229
+ const colors = getThemeColors(themeConfig);
297
230
  if (colors) {
298
- if (colors.bg) card.style.setProperty("--ds-bg", colors.bg);
299
- if (colors.ink) card.style.setProperty("--ds-fg", colors.ink);
300
- if (colors.muted) card.style.setProperty("--ds-muted", colors.muted);
301
- if (colors.border) card.style.setProperty("--ds-border", colors.border);
302
- if (colors.accent) card.style.setProperty("--ds-accent", colors.accent);
231
+ if (colors.bg) style["--ds-bg"] = colors.bg;
232
+ if (colors.ink) style["--ds-fg"] = colors.ink;
233
+ if (colors.muted) style["--ds-muted"] = colors.muted;
234
+ if (colors.border) style["--ds-border"] = colors.border;
235
+ if (colors.accent) style["--ds-accent"] = colors.accent;
303
236
  }
304
- if (maxWidth) card.style.setProperty("--ds-max-width", maxWidth);
237
+ if (maxWidth) style["--ds-max-width"] = maxWidth;
238
+ return style;
239
+ }
240
+ function DailySoupWidget({ lang, themeConfig, scheduleUrl, maxWidth }) {
241
+ const [resolvedTheme, setResolvedTheme] = (0, import_react.useState)(() => resolveTheme(themeConfig));
242
+ const [schedule, setSchedule] = (0, import_react.useState)(null);
243
+ const [displayedDate, setDisplayedDate] = (0, import_react.useState)("");
244
+ const [quote, setQuote] = (0, import_react.useState)(null);
245
+ const [loadFailed, setLoadFailed] = (0, import_react.useState)(false);
246
+ const [copyToast, setCopyToast] = (0, import_react.useState)(false);
247
+ (0, import_react.useEffect)(() => {
248
+ if (themeConfig !== "auto") {
249
+ setResolvedTheme(resolveTheme(themeConfig));
250
+ return;
251
+ }
252
+ setResolvedTheme(resolveTheme(themeConfig));
253
+ return watchSystemTheme(setResolvedTheme);
254
+ }, [themeConfig]);
255
+ (0, import_react.useEffect)(() => {
256
+ let cancelled = false;
257
+ let retried = false;
258
+ const load = async () => {
259
+ const sch = await fetchSchedule(scheduleUrl, lang);
260
+ if (cancelled) return;
261
+ if (!sch) {
262
+ if (!retried) {
263
+ retried = true;
264
+ setTimeout(load, 2e3);
265
+ return;
266
+ }
267
+ setLoadFailed(true);
268
+ return;
269
+ }
270
+ const today = todayUtc8();
271
+ const q = pickQuote(sch, today);
272
+ if (!q) {
273
+ setLoadFailed(true);
274
+ return;
275
+ }
276
+ (0, import_react_dom.flushSync)(() => {
277
+ setSchedule(sch);
278
+ setDisplayedDate(today);
279
+ setQuote(q);
280
+ });
281
+ };
282
+ load();
283
+ return () => {
284
+ cancelled = true;
285
+ };
286
+ }, [lang, scheduleUrl]);
287
+ (0, import_react.useEffect)(() => {
288
+ if (!schedule || typeof document === "undefined") return;
289
+ const onVisibility = () => {
290
+ if (document.visibilityState !== "visible") return;
291
+ const today = todayUtc8();
292
+ if (today === displayedDate) return;
293
+ const q = pickQuote(schedule, today);
294
+ if (!q) return;
295
+ (0, import_react_dom.flushSync)(() => {
296
+ setDisplayedDate(today);
297
+ setQuote(q);
298
+ });
299
+ };
300
+ document.addEventListener("visibilitychange", onVisibility);
301
+ return () => document.removeEventListener("visibilitychange", onVisibility);
302
+ }, [schedule, displayedDate]);
303
+ const inlineStyle = buildInlineStyle(themeConfig, maxWidth);
304
+ const s = t(lang);
305
+ const handleCopy = (0, import_react.useCallback)(async () => {
306
+ if (!quote) return;
307
+ const ok = await copyToClipboard({ text: quote.text, author: quote.author });
308
+ if (ok) {
309
+ setCopyToast(true);
310
+ setTimeout(() => setCopyToast(false), 2e3);
311
+ }
312
+ }, [quote]);
313
+ if (loadFailed) {
314
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
315
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: WIDGET_STYLES }),
316
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: `ds-card ds-${resolvedTheme}`, style: inlineStyle, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "ds-error", children: s.loadFailed }) })
317
+ ] });
318
+ }
319
+ if (!quote) {
320
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
321
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: WIDGET_STYLES }),
322
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `ds-card ds-${resolvedTheme} ds-skeleton`, style: inlineStyle, children: [
323
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ds-quote", children: "\xA0".repeat(25) }),
324
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ds-meta", children: [
325
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "ds-author", children: "\xA0" }),
326
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "ds-source", children: "\xA0" })
327
+ ] })
328
+ ] })
329
+ ] });
330
+ }
331
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
332
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: WIDGET_STYLES }),
333
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `ds-card ds-${resolvedTheme}`, style: inlineStyle, children: [
334
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "ds-quote", children: quote.text }),
335
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ds-meta", children: [
336
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "ds-author", children: [
337
+ "\u2014 ",
338
+ quote.author,
339
+ quote.attribution === "popular-attribution" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "ds-flag", children: s.attributedPopular })
340
+ ] }),
341
+ quote.source && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "ds-source", children: [
342
+ s.source,
343
+ "\uFF1A",
344
+ quote.sourceUrl ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: quote.sourceUrl, target: "_blank", rel: "noopener noreferrer", children: quote.source }) : quote.source
345
+ ] })
346
+ ] }),
347
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ds-actions", children: [
348
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ds-share", children: [
349
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
350
+ "button",
351
+ {
352
+ className: `ds-btn${copyToast ? " ds-toast" : ""}`,
353
+ "data-action": "copy",
354
+ type: "button",
355
+ "aria-label": s.copy,
356
+ onClick: handleCopy,
357
+ children: [
358
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "aria-hidden": "true", children: "\u29C9" }),
359
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "ds-share-label", children: copyToast ? s.copied : s.copy })
360
+ ]
361
+ }
362
+ ),
363
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
364
+ "a",
365
+ {
366
+ className: "ds-btn",
367
+ "data-action": "x",
368
+ href: buildXShareUrl({ text: quote.text, author: quote.author }),
369
+ target: "_blank",
370
+ rel: "noopener noreferrer",
371
+ "aria-label": s.shareX,
372
+ children: [
373
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "aria-hidden": "true", children: "\u{1D54F}" }),
374
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "ds-share-label", children: "X" })
375
+ ]
376
+ }
377
+ ),
378
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
379
+ "a",
380
+ {
381
+ className: "ds-btn",
382
+ "data-action": "line",
383
+ href: buildLineShareUrl({ text: quote.text, author: quote.author }),
384
+ target: "_blank",
385
+ rel: "noopener noreferrer",
386
+ "aria-label": s.shareLine,
387
+ children: [
388
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "aria-hidden": "true", children: "L" }),
389
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "ds-share-label", children: "LINE" })
390
+ ]
391
+ }
392
+ )
393
+ ] }),
394
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "ds-powered", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
395
+ "a",
396
+ {
397
+ href: "https://personal-site-mocha-chi.vercel.app",
398
+ target: "_blank",
399
+ rel: "noopener noreferrer",
400
+ children: s.poweredBy
401
+ }
402
+ ) })
403
+ ] })
404
+ ] })
405
+ ] });
406
+ }
407
+ function attachRoot(host) {
408
+ if (typeof host.attachShadow === "function") {
409
+ if (host.shadowRoot) return host.shadowRoot;
410
+ return host.attachShadow({ mode: "open" });
411
+ }
412
+ console.warn("[daily-soup] attachShadow unsupported, falling back to light DOM");
413
+ return host;
305
414
  }
306
415
  function mount(host, options = {}) {
307
416
  const lang = options.lang ?? "zh";
308
417
  const themeConfig = options.theme ?? "auto";
309
418
  const scheduleUrl = options.scheduleUrl === void 0 ? DEFAULT_SCHEDULE_BASE : options.scheduleUrl;
310
419
  const maxWidth = options.maxWidth;
311
- let resolvedTheme = resolveTheme(themeConfig);
312
420
  const root = attachRoot(host);
313
421
  if (root === host) {
314
422
  host.textContent = "";
315
423
  } else {
316
- while (root.firstChild) root.removeChild(root.firstChild);
317
- }
318
- injectStyles(root);
319
- const card = buildSkeleton(resolvedTheme);
320
- applyThemeOverrides(card, themeConfig, maxWidth);
321
- root.appendChild(card);
322
- const state = {
323
- lang,
324
- themeConfig,
325
- scheduleUrl,
326
- host,
327
- root,
328
- cardEl: card,
329
- unwatchTheme: () => {
330
- },
331
- unwatchVisibility: () => {
332
- },
333
- schedule: null,
334
- displayedDate: ""
335
- };
336
- if (themeConfig === "auto") {
337
- state.unwatchTheme = watchSystemTheme((t2) => {
338
- resolvedTheme = t2;
339
- state.cardEl.classList.remove("ds-light", "ds-dark");
340
- state.cardEl.classList.add(`ds-${t2}`);
341
- });
342
- }
343
- let cancelled = false;
344
- let retried = false;
345
- const load = async () => {
346
- const schedule = await fetchSchedule(scheduleUrl, lang);
347
- if (cancelled) return;
348
- if (!schedule) {
349
- if (!retried) {
350
- retried = true;
351
- setTimeout(load, 2e3);
352
- return;
353
- }
354
- renderError(state.cardEl, lang, resolvedTheme);
355
- return;
356
- }
357
- state.schedule = schedule;
358
- const quote = pickQuote(schedule);
359
- if (!quote) {
360
- renderError(state.cardEl, lang, resolvedTheme);
361
- return;
362
- }
363
- state.displayedDate = todayUtc8();
364
- renderQuote(state.cardEl, quote, lang, resolvedTheme);
365
- };
366
- load();
367
- if (typeof document !== "undefined") {
368
- const onVisibility = () => {
369
- if (document.visibilityState !== "visible") return;
370
- if (!state.schedule) return;
371
- const today = todayUtc8();
372
- if (today === state.displayedDate) return;
373
- const quote = pickQuote(state.schedule);
374
- if (!quote) return;
375
- state.displayedDate = today;
376
- renderQuote(state.cardEl, quote, lang, resolvedTheme);
377
- };
378
- document.addEventListener("visibilitychange", onVisibility);
379
- state.unwatchVisibility = () => document.removeEventListener("visibilitychange", onVisibility);
424
+ const shadow = root;
425
+ while (shadow.firstChild) shadow.removeChild(shadow.firstChild);
380
426
  }
427
+ const container = document.createElement("div");
428
+ root.appendChild(container);
429
+ const reactRoot = (0, import_client.createRoot)(container);
430
+ (0, import_react_dom.flushSync)(() => {
431
+ reactRoot.render(
432
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
433
+ DailySoupWidget,
434
+ {
435
+ lang,
436
+ themeConfig,
437
+ scheduleUrl,
438
+ maxWidth
439
+ }
440
+ )
441
+ );
442
+ });
381
443
  return {
382
444
  destroy() {
383
- cancelled = true;
384
- state.unwatchTheme();
385
- state.unwatchVisibility();
445
+ reactRoot.unmount();
386
446
  if (root === host) {
387
447
  host.textContent = "";
388
448
  } else if (host.shadowRoot) {
389
- while (host.shadowRoot.firstChild) host.shadowRoot.removeChild(host.shadowRoot.firstChild);
449
+ while (host.shadowRoot.firstChild) {
450
+ host.shadowRoot.removeChild(host.shadowRoot.firstChild);
451
+ }
390
452
  }
391
453
  }
392
454
  };
@@ -406,16 +468,16 @@ function mountAll(selector = "[data-daily-soup], #daily-soup") {
406
468
  }
407
469
 
408
470
  // src/component.tsx
409
- var import_react = require("react");
410
- var import_jsx_runtime = require("react/jsx-runtime");
471
+ var import_react2 = require("react");
472
+ var import_jsx_runtime2 = require("react/jsx-runtime");
411
473
  function DailySoup({ lang = "zh", theme = "auto", scheduleUrl, className, maxWidth }) {
412
- const hostRef = (0, import_react.useRef)(null);
474
+ const hostRef = (0, import_react2.useRef)(null);
413
475
  const themeKey = typeof theme === "string" ? theme : JSON.stringify(theme);
414
- (0, import_react.useEffect)(() => {
476
+ (0, import_react2.useEffect)(() => {
415
477
  if (!hostRef.current) return;
416
478
  const handle = mount(hostRef.current, { lang, theme, scheduleUrl, maxWidth });
417
479
  return () => handle.destroy();
418
480
  }, [lang, themeKey, scheduleUrl, maxWidth]);
419
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: hostRef, className, "data-daily-soup-host": "" });
481
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ref: hostRef, className, "data-daily-soup-host": "" });
420
482
  }
421
483
  //# sourceMappingURL=embed.cjs.js.map