daily-soup-widget 0.2.1 → 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: {
@@ -155,9 +160,9 @@ var WIDGET_STYLES = `
155
160
  align-items: center;
156
161
  gap: 0.25rem;
157
162
  }
158
- .ds-btn:hover { background: var(--ds-accent); color: var(--ds-bg); border-color: var(--ds-accent); }
163
+ .ds-btn:hover { color: var(--ds-accent); border-color: var(--ds-accent); }
159
164
  .ds-btn:focus-visible { outline: 2px solid var(--ds-accent); outline-offset: 2px; }
160
- .ds-btn.ds-toast { background: var(--ds-accent); color: var(--ds-bg); border-color: var(--ds-accent); }
165
+ .ds-btn.ds-toast { color: var(--ds-accent); border-color: var(--ds-accent); }
161
166
  .ds-powered { font-size: 0.75em; color: var(--ds-muted); }
162
167
  .ds-powered a { color: var(--ds-muted); text-decoration: none; }
163
168
  .ds-powered a:hover { text-decoration: underline; }
@@ -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,80 +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;
236
+ }
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
+ ] });
303
330
  }
304
- if (maxWidth) card.style.setProperty("--ds-max-width", maxWidth);
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);
424
+ const shadow = root;
425
+ while (shadow.firstChild) shadow.removeChild(shadow.firstChild);
317
426
  }
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
- };
332
- if (themeConfig === "auto") {
333
- state.unwatchTheme = watchSystemTheme((t2) => {
334
- resolvedTheme = t2;
335
- state.cardEl.classList.remove("ds-light", "ds-dark");
336
- state.cardEl.classList.add(`ds-${t2}`);
337
- });
338
- }
339
- let cancelled = false;
340
- let retried = false;
341
- const load = async () => {
342
- const schedule = await fetchSchedule(scheduleUrl, lang);
343
- if (cancelled) return;
344
- if (!schedule) {
345
- if (!retried) {
346
- retried = true;
347
- setTimeout(load, 2e3);
348
- return;
349
- }
350
- renderError(state.cardEl, lang, resolvedTheme);
351
- return;
352
- }
353
- const quote = pickQuote(schedule);
354
- if (!quote) {
355
- renderError(state.cardEl, lang, resolvedTheme);
356
- return;
357
- }
358
- renderQuote(state.cardEl, quote, lang, resolvedTheme);
359
- };
360
- load();
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
+ });
361
443
  return {
362
444
  destroy() {
363
- cancelled = true;
364
- state.unwatchTheme();
445
+ reactRoot.unmount();
365
446
  if (root === host) {
366
447
  host.textContent = "";
367
448
  } else if (host.shadowRoot) {
368
- while (host.shadowRoot.firstChild) host.shadowRoot.removeChild(host.shadowRoot.firstChild);
449
+ while (host.shadowRoot.firstChild) {
450
+ host.shadowRoot.removeChild(host.shadowRoot.firstChild);
451
+ }
369
452
  }
370
453
  }
371
454
  };
@@ -385,16 +468,16 @@ function mountAll(selector = "[data-daily-soup], #daily-soup") {
385
468
  }
386
469
 
387
470
  // src/component.tsx
388
- var import_react = require("react");
389
- var import_jsx_runtime = require("react/jsx-runtime");
471
+ var import_react2 = require("react");
472
+ var import_jsx_runtime2 = require("react/jsx-runtime");
390
473
  function DailySoup({ lang = "zh", theme = "auto", scheduleUrl, className, maxWidth }) {
391
- const hostRef = (0, import_react.useRef)(null);
474
+ const hostRef = (0, import_react2.useRef)(null);
392
475
  const themeKey = typeof theme === "string" ? theme : JSON.stringify(theme);
393
- (0, import_react.useEffect)(() => {
476
+ (0, import_react2.useEffect)(() => {
394
477
  if (!hostRef.current) return;
395
478
  const handle = mount(hostRef.current, { lang, theme, scheduleUrl, maxWidth });
396
479
  return () => handle.destroy();
397
480
  }, [lang, themeKey, scheduleUrl, maxWidth]);
398
- 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": "" });
399
482
  }
400
483
  //# sourceMappingURL=embed.cjs.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/index.ts", "../src/i18n.ts", "../src/theme.ts", "../src/share.ts", "../src/styles.ts", "../src/date.ts", "../src/widget.ts", "../src/component.tsx"],
4
- "sourcesContent": ["export { mount, mountAll } from './widget';\nexport { DailySoup } from './component';\nexport type {\n Lang,\n ThemeConfig,\n ResolvedTheme,\n Quote,\n Schedule,\n MountOptions,\n DailySoupProps,\n} from './types';\n", "import type { Lang } from './types';\n\ninterface UiStrings {\n copy: string;\n copied: string;\n share: string;\n source: string;\n poweredBy: string;\n attributedPopular: string;\n shareX: string;\n shareLine: string;\n loadFailed: string;\n}\n\nconst STRINGS: Record<Lang, UiStrings> = {\n zh: {\n copy: '\u8907\u88FD',\n copied: '\u5DF2\u8907\u88FD',\n share: '\u5206\u4EAB',\n source: '\u51FA\u8655',\n poweredBy: '\u7531 mshmwr \u63D0\u4F9B',\n attributedPopular: '\u50B3\u7D71\u6B78\u5C6C',\n shareX: '\u5206\u4EAB\u5230 X',\n shareLine: '\u5206\u4EAB\u5230 LINE',\n loadFailed: '\u672C\u65E5\u5C0F\u8A9E\u8F09\u5165\u5931\u6557',\n },\n};\n\nexport function t(lang: Lang): UiStrings {\n return STRINGS[lang] ?? STRINGS.zh;\n}\n\nexport type { UiStrings };\n", "import type { ThemeConfig, ResolvedTheme, ThemeColors } from './types';\n\nfunction isThemeColors(config: ThemeConfig): config is ThemeColors {\n return typeof config === 'object' && config !== null;\n}\n\nexport function resolveTheme(config: ThemeConfig): ResolvedTheme {\n if (isThemeColors(config)) return config.base ?? 'light';\n if (config === 'light' || config === 'dark') return config;\n if (typeof window === 'undefined' || !window.matchMedia) return 'light';\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n}\n\nexport function getThemeColors(config: ThemeConfig): ThemeColors | null {\n return isThemeColors(config) ? config : null;\n}\n\nexport function watchSystemTheme(cb: (theme: ResolvedTheme) => void): () => void {\n if (typeof window === 'undefined' || !window.matchMedia) return () => {};\n const mql = window.matchMedia('(prefers-color-scheme: dark)');\n const handler = (e: MediaQueryListEvent) => cb(e.matches ? 'dark' : 'light');\n mql.addEventListener('change', handler);\n return () => mql.removeEventListener('change', handler);\n}\n", "const SHARE_URL = 'https://daily-soup-widget.vercel.app';\n\nexport interface ShareContent {\n text: string;\n author: string;\n}\n\nexport async function copyToClipboard(content: ShareContent): Promise<boolean> {\n const payload = `${content.text} \u2014 ${content.author}`;\n if (typeof navigator !== 'undefined' && navigator.clipboard) {\n try {\n await navigator.clipboard.writeText(payload);\n return true;\n } catch {\n return false;\n }\n }\n return false;\n}\n\nexport function buildXShareUrl(content: ShareContent): string {\n const text = encodeURIComponent(`${content.text} \u2014 ${content.author}`);\n const url = encodeURIComponent(SHARE_URL);\n return `https://twitter.com/intent/tweet?text=${text}&url=${url}`;\n}\n\nexport function buildLineShareUrl(content: ShareContent): string {\n const url = encodeURIComponent(`${SHARE_URL} \u2014 ${content.text}`);\n return `https://social-plugins.line.me/lineit/share?url=${url}`;\n}\n", "export const WIDGET_STYLES = `\n :host { all: initial; display: block; font-family: system-ui, -apple-system, \"Segoe UI\", Roboto, \"Noto Sans TC\", sans-serif; }\n * { box-sizing: border-box; }\n .ds-card {\n container-type: inline-size;\n width: 100%;\n max-width: var(--ds-max-width, 32rem);\n margin: 0 auto;\n padding: 1.25rem 1.5rem;\n border-radius: 0.75rem;\n border: 1px solid var(--ds-border);\n background: var(--ds-bg);\n color: var(--ds-fg);\n font-size: clamp(0.875rem, 2.5cqi, 1.25rem);\n line-height: 1.7;\n transition: background 0.2s ease, color 0.2s ease;\n }\n .ds-card.ds-light {\n --ds-bg: #fdfcf7;\n --ds-fg: #1f2933;\n --ds-accent: #5b6b9e;\n --ds-muted: #6b7280;\n --ds-border: #e5e7eb;\n }\n .ds-card.ds-dark {\n --ds-bg: #1a1d24;\n --ds-fg: #e5e7eb;\n --ds-accent: #9aa9d4;\n --ds-muted: #9ca3af;\n --ds-border: #2d323d;\n }\n .ds-quote {\n margin: 0 0 0.875rem;\n font-size: 1.1em;\n font-weight: 500;\n letter-spacing: 0.01em;\n white-space: pre-wrap;\n }\n .ds-quote::before { content: '\\\\201C'; margin-right: 0.15em; color: var(--ds-accent); }\n .ds-quote::after { content: '\\\\201D'; margin-left: 0.15em; color: var(--ds-accent); }\n .ds-meta { display: flex; flex-direction: column; gap: 0.15rem; margin-bottom: 0.875rem; font-size: 0.875em; color: var(--ds-muted); }\n .ds-author { font-weight: 500; color: var(--ds-fg); }\n .ds-source { font-size: 0.95em; }\n .ds-source a { color: var(--ds-accent); text-decoration: none; }\n .ds-source a:hover { text-decoration: underline; }\n .ds-flag { display: inline-block; margin-left: 0.25rem; padding: 0 0.4rem; font-size: 0.75em; border: 1px solid var(--ds-border); border-radius: 9999px; color: var(--ds-muted); }\n .ds-actions { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; margin-top: 0.875rem; padding-top: 0.875rem; border-top: 1px solid var(--ds-border); }\n .ds-share { display: flex; gap: 0.35rem; }\n .ds-btn {\n appearance: none;\n background: transparent;\n color: var(--ds-fg);\n border: 1px solid var(--ds-border);\n border-radius: 0.5rem;\n padding: 0.3rem 0.7rem;\n font-size: 0.85em;\n font-family: inherit;\n cursor: pointer;\n transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;\n text-decoration: none;\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n }\n .ds-btn:hover { background: var(--ds-accent); color: var(--ds-bg); border-color: var(--ds-accent); }\n .ds-btn:focus-visible { outline: 2px solid var(--ds-accent); outline-offset: 2px; }\n .ds-btn.ds-toast { background: var(--ds-accent); color: var(--ds-bg); border-color: var(--ds-accent); }\n .ds-powered { font-size: 0.75em; color: var(--ds-muted); }\n .ds-powered a { color: var(--ds-muted); text-decoration: none; }\n .ds-powered a:hover { text-decoration: underline; }\n .ds-skeleton .ds-quote { background: var(--ds-border); border-radius: 0.25rem; color: transparent; }\n .ds-skeleton .ds-quote::before, .ds-skeleton .ds-quote::after { content: ''; }\n .ds-error { color: var(--ds-muted); font-size: 0.875em; }\n\n @container (max-width: 320px) {\n .ds-card { padding: 1rem 1.1rem; }\n .ds-share-label { display: none; }\n .ds-actions { flex-wrap: wrap; }\n }\n @container (min-width: 500px) {\n .ds-quote { font-size: 1.25em; }\n .ds-meta { flex-direction: row; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.75rem; }\n }\n @container (min-width: 700px) {\n .ds-card { padding: 1.75rem 2rem; }\n .ds-quote { font-size: 1.35em; margin-bottom: 1.125rem; }\n .ds-meta { gap: 0.25rem 1rem; margin-bottom: 1.125rem; }\n .ds-actions { margin-top: 1.125rem; padding-top: 1.125rem; }\n }\n @media (prefers-reduced-motion: reduce) {\n .ds-card, .ds-btn { transition: none; }\n }\n`;\n", "export function todayUtc8(now: Date = new Date()): string {\n const utc8 = new Date(now.getTime() + 8 * 60 * 60 * 1000);\n const yy = utc8.getUTCFullYear();\n const mm = String(utc8.getUTCMonth() + 1).padStart(2, '0');\n const dd = String(utc8.getUTCDate()).padStart(2, '0');\n return `${yy}-${mm}-${dd}`;\n}\n", "import type { Lang, MountOptions, Quote, ResolvedTheme, Schedule, ThemeConfig } from './types';\nimport { t } from './i18n';\nimport { resolveTheme, watchSystemTheme, getThemeColors } from './theme';\nimport { buildLineShareUrl, buildXShareUrl, copyToClipboard } from './share';\nimport { WIDGET_STYLES } from './styles';\nimport { todayUtc8 } from './date';\n\nconst DEFAULT_SCHEDULE_BASE = 'https://daily-soup-widget.vercel.app';\n\nexport interface MountHandle {\n destroy(): void;\n}\n\ninterface WidgetState {\n lang: Lang;\n themeConfig: ThemeConfig;\n scheduleUrl: string;\n host: HTMLElement;\n root: ShadowRoot | HTMLElement;\n cardEl: HTMLElement;\n unwatchTheme: () => void;\n}\n\nfunction escape(s: string): string {\n return s\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\n}\n\nfunction attachRoot(host: HTMLElement): ShadowRoot | HTMLElement {\n if (typeof host.attachShadow === 'function') {\n if (host.shadowRoot) return host.shadowRoot;\n return host.attachShadow({ mode: 'open' });\n }\n console.warn('[daily-soup] attachShadow unsupported, falling back to light DOM');\n return host;\n}\n\nfunction injectStyles(root: ShadowRoot | HTMLElement) {\n const style = document.createElement('style');\n style.textContent = WIDGET_STYLES;\n root.appendChild(style);\n}\n\nfunction buildSkeleton(theme: ResolvedTheme): HTMLElement {\n const card = document.createElement('div');\n card.className = `ds-card ds-${theme} ds-skeleton`;\n card.innerHTML = `\n <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>\n <div class=\"ds-meta\"><span class=\"ds-author\">&nbsp;</span><span class=\"ds-source\">&nbsp;</span></div>\n `;\n return card;\n}\n\nfunction renderQuote(card: HTMLElement, quote: Quote, lang: Lang, theme: ResolvedTheme) {\n const s = t(lang);\n card.className = `ds-card ds-${theme}`;\n const sourceLabel = quote.sourceUrl\n ? `<a href=\"${escape(quote.sourceUrl)}\" target=\"_blank\" rel=\"noopener noreferrer\">${escape(quote.source)}</a>`\n : escape(quote.source);\n const flag = quote.attribution === 'popular-attribution'\n ? `<span class=\"ds-flag\">${escape(s.attributedPopular)}</span>`\n : '';\n card.innerHTML = `\n <p class=\"ds-quote\">${escape(quote.text)}</p>\n <div class=\"ds-meta\">\n <span class=\"ds-author\">\u2014 ${escape(quote.author)}${flag}</span>\n ${quote.source ? `<span class=\"ds-source\">${s.source}\uFF1A${sourceLabel}</span>` : ''}\n </div>\n <div class=\"ds-actions\">\n <div class=\"ds-share\">\n <button class=\"ds-btn\" data-action=\"copy\" type=\"button\" aria-label=\"${escape(s.copy)}\">\n <span aria-hidden=\"true\">\u29C9</span><span class=\"ds-share-label\">${escape(s.copy)}</span>\n </button>\n <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)}\">\n <span aria-hidden=\"true\">\uD835\uDD4F</span><span class=\"ds-share-label\">X</span>\n </a>\n <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)}\">\n <span aria-hidden=\"true\">L</span><span class=\"ds-share-label\">LINE</span>\n </a>\n </div>\n <span class=\"ds-powered\"><a href=\"https://personal-site-mocha-chi.vercel.app\" target=\"_blank\" rel=\"noopener noreferrer\">${s.poweredBy}</a></span>\n </div>\n `;\n\n const copyBtn = card.querySelector<HTMLButtonElement>('[data-action=\"copy\"]');\n if (copyBtn) {\n copyBtn.addEventListener('click', async () => {\n const ok = await copyToClipboard({ text: quote.text, author: quote.author });\n if (ok) {\n const label = copyBtn.querySelector('.ds-share-label');\n const originalLabel = label?.textContent ?? '';\n if (label) label.textContent = s.copied;\n copyBtn.classList.add('ds-toast');\n setTimeout(() => {\n if (label) label.textContent = originalLabel;\n copyBtn.classList.remove('ds-toast');\n }, 2000);\n }\n });\n }\n}\n\nfunction renderError(card: HTMLElement, lang: Lang, theme: ResolvedTheme) {\n card.className = `ds-card ds-${theme}`;\n const s = t(lang);\n card.innerHTML = `<p class=\"ds-error\">${escape(s.loadFailed)}</p>`;\n}\n\nasync function fetchSchedule(scheduleUrl: string, lang: Lang): Promise<Schedule | null> {\n const base = scheduleUrl.replace(/\\/$/, '');\n const url = `${base}/schedule-${lang}.json`;\n try {\n const init: RequestInit = base === '' ? { credentials: 'omit' } : { credentials: 'omit', mode: 'cors' };\n const res = await fetch(url, init);\n if (!res.ok) return null;\n return (await res.json()) as Schedule;\n } catch {\n return null;\n }\n}\n\nfunction pickQuote(schedule: Schedule): Quote | null {\n const today = todayUtc8();\n const id = schedule.entries[today];\n if (id && schedule.quotes[id]) return schedule.quotes[id];\n const fallbackId = Object.keys(schedule.quotes).sort()[0];\n if (fallbackId && schedule.quotes[fallbackId]) {\n console.warn('[daily-soup] today entry missing or stale, falling back to first quote');\n return schedule.quotes[fallbackId];\n }\n return null;\n}\n\nfunction applyThemeOverrides(card: HTMLElement, config: ThemeConfig, maxWidth?: string) {\n const colors = getThemeColors(config);\n if (colors) {\n if (colors.bg) card.style.setProperty('--ds-bg', colors.bg);\n if (colors.ink) card.style.setProperty('--ds-fg', colors.ink);\n if (colors.muted) card.style.setProperty('--ds-muted', colors.muted);\n if (colors.border) card.style.setProperty('--ds-border', colors.border);\n if (colors.accent) card.style.setProperty('--ds-accent', colors.accent);\n }\n if (maxWidth) card.style.setProperty('--ds-max-width', maxWidth);\n}\n\nexport function mount(host: HTMLElement, options: MountOptions = {}): MountHandle {\n const lang: Lang = options.lang ?? 'zh';\n const themeConfig: ThemeConfig = options.theme ?? 'auto';\n const scheduleUrl = options.scheduleUrl === undefined ? DEFAULT_SCHEDULE_BASE : options.scheduleUrl;\n const maxWidth = options.maxWidth;\n\n let resolvedTheme = resolveTheme(themeConfig);\n const root = attachRoot(host);\n if (root === host) {\n // light DOM fallback \u2014 clear any prior content\n host.textContent = '';\n } else {\n while ((root as ShadowRoot).firstChild) (root as ShadowRoot).removeChild((root as ShadowRoot).firstChild!);\n }\n injectStyles(root);\n\n const card = buildSkeleton(resolvedTheme);\n applyThemeOverrides(card, themeConfig, maxWidth);\n root.appendChild(card);\n\n const state: WidgetState = {\n lang,\n themeConfig,\n scheduleUrl,\n host,\n root,\n cardEl: card,\n unwatchTheme: () => {},\n };\n\n if (themeConfig === 'auto') {\n state.unwatchTheme = watchSystemTheme((t) => {\n resolvedTheme = t;\n state.cardEl.classList.remove('ds-light', 'ds-dark');\n state.cardEl.classList.add(`ds-${t}`);\n });\n }\n\n let cancelled = false;\n let retried = false;\n\n const load = async () => {\n const schedule = await fetchSchedule(scheduleUrl, lang);\n if (cancelled) return;\n if (!schedule) {\n if (!retried) {\n retried = true;\n setTimeout(load, 2000);\n return;\n }\n renderError(state.cardEl, lang, resolvedTheme);\n return;\n }\n const quote = pickQuote(schedule);\n if (!quote) {\n renderError(state.cardEl, lang, resolvedTheme);\n return;\n }\n renderQuote(state.cardEl, quote, lang, resolvedTheme);\n };\n load();\n\n return {\n destroy() {\n cancelled = true;\n state.unwatchTheme();\n if (root === host) {\n host.textContent = '';\n } else if (host.shadowRoot) {\n while (host.shadowRoot.firstChild) host.shadowRoot.removeChild(host.shadowRoot.firstChild);\n }\n },\n };\n}\n\nexport function mountAll(selector = '[data-daily-soup], #daily-soup'): MountHandle[] {\n if (typeof document === 'undefined') return [];\n const nodes = document.querySelectorAll<HTMLElement>(selector);\n const handles: MountHandle[] = [];\n nodes.forEach((node) => {\n const lang = (node.dataset.lang as Lang | undefined) ?? 'zh';\n const theme = (node.dataset.theme as 'auto' | 'light' | 'dark' | undefined) ?? 'auto';\n const scheduleUrl = node.dataset.scheduleUrl;\n const maxWidth = node.dataset.maxWidth;\n handles.push(mount(node, { lang, theme, scheduleUrl, maxWidth }));\n });\n return handles;\n}\n", "import { useEffect, useRef } from 'react';\nimport { mount } from './widget';\nimport type { DailySoupProps } from './types';\n\nexport function DailySoup({ lang = 'zh', theme = 'auto', scheduleUrl, className, maxWidth }: DailySoupProps) {\n const hostRef = useRef<HTMLDivElement | null>(null);\n\n const themeKey = typeof theme === 'string' ? theme : JSON.stringify(theme);\n useEffect(() => {\n if (!hostRef.current) return;\n const handle = mount(hostRef.current, { lang, theme, scheduleUrl, maxWidth });\n return () => handle.destroy();\n }, [lang, themeKey, scheduleUrl, maxWidth]);\n\n return <div ref={hostRef} className={className} data-daily-soup-host=\"\" />;\n}\n"],
5
- "mappings": "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",
6
- "names": ["t"]
3
+ "sources": ["../src/index.ts", "../src/widget.tsx", "../src/i18n.ts", "../src/theme.ts", "../src/share.ts", "../src/styles.ts", "../src/date.ts", "../src/component.tsx"],
4
+ "sourcesContent": ["export { mount, mountAll } from './widget';\nexport { DailySoup } from './component';\nexport type {\n Lang,\n ThemeConfig,\n ResolvedTheme,\n Quote,\n Schedule,\n MountOptions,\n DailySoupProps,\n} from './types';\n", "import { useCallback, useEffect, useState } from 'react';\nimport { createRoot, type Root } from 'react-dom/client';\nimport { flushSync } from 'react-dom';\nimport type { Lang, MountOptions, Quote, ResolvedTheme, Schedule, ThemeConfig } from './types';\nimport { t } from './i18n';\nimport { resolveTheme, watchSystemTheme, getThemeColors } from './theme';\nimport { buildLineShareUrl, buildXShareUrl, copyToClipboard } from './share';\nimport { WIDGET_STYLES } from './styles';\nimport { todayUtc8 } from './date';\n\nconst DEFAULT_SCHEDULE_BASE = 'https://daily-soup-widget.vercel.app';\n\nexport interface MountHandle {\n destroy(): void;\n}\n\ninterface WidgetProps {\n lang: Lang;\n themeConfig: ThemeConfig;\n scheduleUrl: string;\n maxWidth?: string;\n}\n\nasync function fetchSchedule(scheduleUrl: string, lang: Lang): Promise<Schedule | null> {\n const base = scheduleUrl.replace(/\\/$/, '');\n const url = `${base}/schedule-${lang}.json`;\n try {\n const init: RequestInit = base === '' ? { credentials: 'omit' } : { credentials: 'omit', mode: 'cors' };\n const res = await fetch(url, init);\n if (!res.ok) return null;\n return (await res.json()) as Schedule;\n } catch {\n return null;\n }\n}\n\nfunction pickQuote(schedule: Schedule, today: string): Quote | null {\n const id = schedule.entries[today];\n if (id && schedule.quotes[id]) return schedule.quotes[id];\n const fallbackId = Object.keys(schedule.quotes).sort()[0];\n if (fallbackId && schedule.quotes[fallbackId]) {\n console.warn('[daily-soup] today entry missing or stale, falling back to first quote');\n return schedule.quotes[fallbackId];\n }\n return null;\n}\n\nfunction buildInlineStyle(themeConfig: ThemeConfig, maxWidth?: string): React.CSSProperties {\n const style: Record<string, string> = {};\n const colors = getThemeColors(themeConfig);\n if (colors) {\n if (colors.bg) style['--ds-bg'] = colors.bg;\n if (colors.ink) style['--ds-fg'] = colors.ink;\n if (colors.muted) style['--ds-muted'] = colors.muted;\n if (colors.border) style['--ds-border'] = colors.border;\n if (colors.accent) style['--ds-accent'] = colors.accent;\n }\n if (maxWidth) style['--ds-max-width'] = maxWidth;\n return style as React.CSSProperties;\n}\n\nfunction DailySoupWidget({ lang, themeConfig, scheduleUrl, maxWidth }: WidgetProps) {\n const [resolvedTheme, setResolvedTheme] = useState<ResolvedTheme>(() => resolveTheme(themeConfig));\n const [schedule, setSchedule] = useState<Schedule | null>(null);\n const [displayedDate, setDisplayedDate] = useState<string>('');\n const [quote, setQuote] = useState<Quote | null>(null);\n const [loadFailed, setLoadFailed] = useState(false);\n const [copyToast, setCopyToast] = useState(false);\n\n useEffect(() => {\n if (themeConfig !== 'auto') {\n setResolvedTheme(resolveTheme(themeConfig));\n return;\n }\n setResolvedTheme(resolveTheme(themeConfig));\n return watchSystemTheme(setResolvedTheme);\n }, [themeConfig]);\n\n useEffect(() => {\n let cancelled = false;\n let retried = false;\n\n const load = async () => {\n const sch = await fetchSchedule(scheduleUrl, lang);\n if (cancelled) return;\n if (!sch) {\n if (!retried) {\n retried = true;\n setTimeout(load, 2000);\n return;\n }\n setLoadFailed(true);\n return;\n }\n const today = todayUtc8();\n const q = pickQuote(sch, today);\n if (!q) {\n setLoadFailed(true);\n return;\n }\n flushSync(() => {\n setSchedule(sch);\n setDisplayedDate(today);\n setQuote(q);\n });\n };\n load();\n\n return () => {\n cancelled = true;\n };\n }, [lang, scheduleUrl]);\n\n useEffect(() => {\n if (!schedule || typeof document === 'undefined') return;\n const onVisibility = () => {\n if (document.visibilityState !== 'visible') return;\n const today = todayUtc8();\n if (today === displayedDate) return;\n const q = pickQuote(schedule, today);\n if (!q) return;\n flushSync(() => {\n setDisplayedDate(today);\n setQuote(q);\n });\n };\n document.addEventListener('visibilitychange', onVisibility);\n return () => document.removeEventListener('visibilitychange', onVisibility);\n }, [schedule, displayedDate]);\n\n const inlineStyle = buildInlineStyle(themeConfig, maxWidth);\n const s = t(lang);\n\n const handleCopy = useCallback(async () => {\n if (!quote) return;\n const ok = await copyToClipboard({ text: quote.text, author: quote.author });\n if (ok) {\n setCopyToast(true);\n setTimeout(() => setCopyToast(false), 2000);\n }\n }, [quote]);\n\n if (loadFailed) {\n return (\n <>\n <style>{WIDGET_STYLES}</style>\n <div className={`ds-card ds-${resolvedTheme}`} style={inlineStyle}>\n <p className=\"ds-error\">{s.loadFailed}</p>\n </div>\n </>\n );\n }\n\n if (!quote) {\n return (\n <>\n <style>{WIDGET_STYLES}</style>\n <div className={`ds-card ds-${resolvedTheme} ds-skeleton`} style={inlineStyle}>\n <div className=\"ds-quote\">{'\u00A0'.repeat(25)}</div>\n <div className=\"ds-meta\">\n <span className=\"ds-author\">{'\u00A0'}</span>\n <span className=\"ds-source\">{'\u00A0'}</span>\n </div>\n </div>\n </>\n );\n }\n\n return (\n <>\n <style>{WIDGET_STYLES}</style>\n <div className={`ds-card ds-${resolvedTheme}`} style={inlineStyle}>\n <p className=\"ds-quote\">{quote.text}</p>\n <div className=\"ds-meta\">\n <span className=\"ds-author\">\n \u2014 {quote.author}\n {quote.attribution === 'popular-attribution' && (\n <span className=\"ds-flag\">{s.attributedPopular}</span>\n )}\n </span>\n {quote.source && (\n <span className=\"ds-source\">\n {s.source}\uFF1A\n {quote.sourceUrl ? (\n <a href={quote.sourceUrl} target=\"_blank\" rel=\"noopener noreferrer\">\n {quote.source}\n </a>\n ) : (\n quote.source\n )}\n </span>\n )}\n </div>\n <div className=\"ds-actions\">\n <div className=\"ds-share\">\n <button\n className={`ds-btn${copyToast ? ' ds-toast' : ''}`}\n data-action=\"copy\"\n type=\"button\"\n aria-label={s.copy}\n onClick={handleCopy}\n >\n <span aria-hidden=\"true\">\u29C9</span>\n <span className=\"ds-share-label\">{copyToast ? s.copied : s.copy}</span>\n </button>\n <a\n className=\"ds-btn\"\n data-action=\"x\"\n href={buildXShareUrl({ text: quote.text, author: quote.author })}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n aria-label={s.shareX}\n >\n <span aria-hidden=\"true\">\uD835\uDD4F</span>\n <span className=\"ds-share-label\">X</span>\n </a>\n <a\n className=\"ds-btn\"\n data-action=\"line\"\n href={buildLineShareUrl({ text: quote.text, author: quote.author })}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n aria-label={s.shareLine}\n >\n <span aria-hidden=\"true\">L</span>\n <span className=\"ds-share-label\">LINE</span>\n </a>\n </div>\n <span className=\"ds-powered\">\n <a\n href=\"https://personal-site-mocha-chi.vercel.app\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {s.poweredBy}\n </a>\n </span>\n </div>\n </div>\n </>\n );\n}\n\nfunction attachRoot(host: HTMLElement): ShadowRoot | HTMLElement {\n if (typeof host.attachShadow === 'function') {\n if (host.shadowRoot) return host.shadowRoot;\n return host.attachShadow({ mode: 'open' });\n }\n console.warn('[daily-soup] attachShadow unsupported, falling back to light DOM');\n return host;\n}\n\nexport function mount(host: HTMLElement, options: MountOptions = {}): MountHandle {\n const lang: Lang = options.lang ?? 'zh';\n const themeConfig: ThemeConfig = options.theme ?? 'auto';\n const scheduleUrl =\n options.scheduleUrl === undefined ? DEFAULT_SCHEDULE_BASE : options.scheduleUrl;\n const maxWidth = options.maxWidth;\n\n const root = attachRoot(host);\n if (root === host) {\n host.textContent = '';\n } else {\n const shadow = root as ShadowRoot;\n while (shadow.firstChild) shadow.removeChild(shadow.firstChild);\n }\n\n const container = document.createElement('div');\n root.appendChild(container);\n const reactRoot: Root = createRoot(container);\n flushSync(() => {\n reactRoot.render(\n <DailySoupWidget\n lang={lang}\n themeConfig={themeConfig}\n scheduleUrl={scheduleUrl}\n maxWidth={maxWidth}\n />\n );\n });\n\n return {\n destroy() {\n reactRoot.unmount();\n if (root === host) {\n host.textContent = '';\n } else if (host.shadowRoot) {\n while (host.shadowRoot.firstChild) {\n host.shadowRoot.removeChild(host.shadowRoot.firstChild);\n }\n }\n },\n };\n}\n\nexport function mountAll(selector = '[data-daily-soup], #daily-soup'): MountHandle[] {\n if (typeof document === 'undefined') return [];\n const nodes = document.querySelectorAll<HTMLElement>(selector);\n const handles: MountHandle[] = [];\n nodes.forEach((node) => {\n const lang = (node.dataset.lang as Lang | undefined) ?? 'zh';\n const theme = (node.dataset.theme as 'auto' | 'light' | 'dark' | undefined) ?? 'auto';\n const scheduleUrl = node.dataset.scheduleUrl;\n const maxWidth = node.dataset.maxWidth;\n handles.push(mount(node, { lang, theme, scheduleUrl, maxWidth }));\n });\n return handles;\n}\n", "import type { Lang } from './types';\n\ninterface UiStrings {\n copy: string;\n copied: string;\n share: string;\n source: string;\n poweredBy: string;\n attributedPopular: string;\n shareX: string;\n shareLine: string;\n loadFailed: string;\n}\n\nconst STRINGS: Record<Lang, UiStrings> = {\n zh: {\n copy: '\u8907\u88FD',\n copied: '\u5DF2\u8907\u88FD',\n share: '\u5206\u4EAB',\n source: '\u51FA\u8655',\n poweredBy: '\u7531 mshmwr \u63D0\u4F9B',\n attributedPopular: '\u50B3\u7D71\u6B78\u5C6C',\n shareX: '\u5206\u4EAB\u5230 X',\n shareLine: '\u5206\u4EAB\u5230 LINE',\n loadFailed: '\u672C\u65E5\u5C0F\u8A9E\u8F09\u5165\u5931\u6557',\n },\n};\n\nexport function t(lang: Lang): UiStrings {\n return STRINGS[lang] ?? STRINGS.zh;\n}\n\nexport type { UiStrings };\n", "import type { ThemeConfig, ResolvedTheme, ThemeColors } from './types';\n\nfunction isThemeColors(config: ThemeConfig): config is ThemeColors {\n return typeof config === 'object' && config !== null;\n}\n\nexport function resolveTheme(config: ThemeConfig): ResolvedTheme {\n if (isThemeColors(config)) return config.base ?? 'light';\n if (config === 'light' || config === 'dark') return config;\n if (typeof window === 'undefined' || !window.matchMedia) return 'light';\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n}\n\nexport function getThemeColors(config: ThemeConfig): ThemeColors | null {\n return isThemeColors(config) ? config : null;\n}\n\nexport function watchSystemTheme(cb: (theme: ResolvedTheme) => void): () => void {\n if (typeof window === 'undefined' || !window.matchMedia) return () => {};\n const mql = window.matchMedia('(prefers-color-scheme: dark)');\n const handler = (e: MediaQueryListEvent) => cb(e.matches ? 'dark' : 'light');\n mql.addEventListener('change', handler);\n return () => mql.removeEventListener('change', handler);\n}\n", "const SHARE_URL = 'https://daily-soup-widget.vercel.app';\n\nexport interface ShareContent {\n text: string;\n author: string;\n}\n\nexport async function copyToClipboard(content: ShareContent): Promise<boolean> {\n const payload = `${content.text} \u2014 ${content.author}`;\n if (typeof navigator !== 'undefined' && navigator.clipboard) {\n try {\n await navigator.clipboard.writeText(payload);\n return true;\n } catch {\n return false;\n }\n }\n return false;\n}\n\nexport function buildXShareUrl(content: ShareContent): string {\n const text = encodeURIComponent(`${content.text} \u2014 ${content.author}`);\n const url = encodeURIComponent(SHARE_URL);\n return `https://twitter.com/intent/tweet?text=${text}&url=${url}`;\n}\n\nexport function buildLineShareUrl(content: ShareContent): string {\n const url = encodeURIComponent(`${SHARE_URL} \u2014 ${content.text}`);\n return `https://social-plugins.line.me/lineit/share?url=${url}`;\n}\n", "export const WIDGET_STYLES = `\n :host { all: initial; display: block; font-family: system-ui, -apple-system, \"Segoe UI\", Roboto, \"Noto Sans TC\", sans-serif; }\n * { box-sizing: border-box; }\n .ds-card {\n container-type: inline-size;\n width: 100%;\n max-width: var(--ds-max-width, 32rem);\n margin: 0 auto;\n padding: 1.25rem 1.5rem;\n border-radius: 0.75rem;\n border: 1px solid var(--ds-border);\n background: var(--ds-bg);\n color: var(--ds-fg);\n font-size: clamp(0.875rem, 2.5cqi, 1.25rem);\n line-height: 1.7;\n transition: background 0.2s ease, color 0.2s ease;\n }\n .ds-card.ds-light {\n --ds-bg: #fdfcf7;\n --ds-fg: #1f2933;\n --ds-accent: #5b6b9e;\n --ds-muted: #6b7280;\n --ds-border: #e5e7eb;\n }\n .ds-card.ds-dark {\n --ds-bg: #1a1d24;\n --ds-fg: #e5e7eb;\n --ds-accent: #9aa9d4;\n --ds-muted: #9ca3af;\n --ds-border: #2d323d;\n }\n .ds-quote {\n margin: 0 0 0.875rem;\n font-size: 1.1em;\n font-weight: 500;\n letter-spacing: 0.01em;\n white-space: pre-wrap;\n }\n .ds-quote::before { content: '\\\\201C'; margin-right: 0.15em; color: var(--ds-accent); }\n .ds-quote::after { content: '\\\\201D'; margin-left: 0.15em; color: var(--ds-accent); }\n .ds-meta { display: flex; flex-direction: column; gap: 0.15rem; margin-bottom: 0.875rem; font-size: 0.875em; color: var(--ds-muted); }\n .ds-author { font-weight: 500; color: var(--ds-fg); }\n .ds-source { font-size: 0.95em; }\n .ds-source a { color: var(--ds-accent); text-decoration: none; }\n .ds-source a:hover { text-decoration: underline; }\n .ds-flag { display: inline-block; margin-left: 0.25rem; padding: 0 0.4rem; font-size: 0.75em; border: 1px solid var(--ds-border); border-radius: 9999px; color: var(--ds-muted); }\n .ds-actions { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; margin-top: 0.875rem; padding-top: 0.875rem; border-top: 1px solid var(--ds-border); }\n .ds-share { display: flex; gap: 0.35rem; }\n .ds-btn {\n appearance: none;\n background: transparent;\n color: var(--ds-fg);\n border: 1px solid var(--ds-border);\n border-radius: 0.5rem;\n padding: 0.3rem 0.7rem;\n font-size: 0.85em;\n font-family: inherit;\n cursor: pointer;\n transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;\n text-decoration: none;\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n }\n .ds-btn:hover { color: var(--ds-accent); border-color: var(--ds-accent); }\n .ds-btn:focus-visible { outline: 2px solid var(--ds-accent); outline-offset: 2px; }\n .ds-btn.ds-toast { color: var(--ds-accent); border-color: var(--ds-accent); }\n .ds-powered { font-size: 0.75em; color: var(--ds-muted); }\n .ds-powered a { color: var(--ds-muted); text-decoration: none; }\n .ds-powered a:hover { text-decoration: underline; }\n .ds-skeleton .ds-quote { background: var(--ds-border); border-radius: 0.25rem; color: transparent; }\n .ds-skeleton .ds-quote::before, .ds-skeleton .ds-quote::after { content: ''; }\n .ds-error { color: var(--ds-muted); font-size: 0.875em; }\n\n @container (max-width: 320px) {\n .ds-card { padding: 1rem 1.1rem; }\n .ds-share-label { display: none; }\n .ds-actions { flex-wrap: wrap; }\n }\n @container (min-width: 500px) {\n .ds-quote { font-size: 1.25em; }\n .ds-meta { flex-direction: row; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.75rem; }\n }\n @container (min-width: 700px) {\n .ds-card { padding: 1.75rem 2rem; }\n .ds-quote { font-size: 1.35em; margin-bottom: 1.125rem; }\n .ds-meta { gap: 0.25rem 1rem; margin-bottom: 1.125rem; }\n .ds-actions { margin-top: 1.125rem; padding-top: 1.125rem; }\n }\n @media (prefers-reduced-motion: reduce) {\n .ds-card, .ds-btn { transition: none; }\n }\n`;\n", "export function todayUtc8(now: Date = new Date()): string {\n const utc8 = new Date(now.getTime() + 8 * 60 * 60 * 1000);\n const yy = utc8.getUTCFullYear();\n const mm = String(utc8.getUTCMonth() + 1).padStart(2, '0');\n const dd = String(utc8.getUTCDate()).padStart(2, '0');\n return `${yy}-${mm}-${dd}`;\n}\n", "import { useEffect, useRef } from 'react';\nimport { mount } from './widget';\nimport type { DailySoupProps } from './types';\n\nexport function DailySoup({ lang = 'zh', theme = 'auto', scheduleUrl, className, maxWidth }: DailySoupProps) {\n const hostRef = useRef<HTMLDivElement | null>(null);\n\n const themeKey = typeof theme === 'string' ? theme : JSON.stringify(theme);\n useEffect(() => {\n if (!hostRef.current) return;\n const handle = mount(hostRef.current, { lang, theme, scheduleUrl, maxWidth });\n return () => handle.destroy();\n }, [lang, themeKey, scheduleUrl, maxWidth]);\n\n return <div ref={hostRef} className={className} data-daily-soup-host=\"\" />;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["import_react", "import_jsx_runtime"]
7
7
  }