yz-yuki-plugin 2.0.3-7 → 2.0.4-1

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/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ # 2.0.4
2
+ * 添加截图列队,优化配置文件注释
3
+
1
4
  # 2.0.3
2
5
  * 优化截图、静态资源引入方式
3
6
  * 优化获取完整文章动态
package/README.md CHANGED
@@ -170,8 +170,8 @@ https://m.weibo.cn/u/7643376782 # 7643376782 为崩坏星穹铁道博主uid
170
170
  ||||
171
171
  | **微博功能** | ------------------------- | ---------- |
172
172
  | 添加微博推送 | 检测博主的微博动态进行推送,权限:Master,可选分类:视频、图文、文章,不加分类则默认全部 | `#订阅微博推送uid` `#订阅微博推送 图文 uid` |
173
- | 取消微博推送 | 删除对应博主的微博对应类型的动态推送,权限:Master,可选分类:视频、图文、文章,不加分类则默认全部 | `#取消微博推送uid` `#取消B站推送 图文 uid` |
174
- | 查看微博订阅列表 | 查看本Bot所有的B站订阅列表,权限:Bot的Master | `#微博全部订阅列表` |
173
+ | 取消微博推送 | 删除对应博主的微博对应类型的动态推送,权限:Master,可选分类:视频、图文、文章,不加分类则默认全部 | `#取消微博推送uid` `#取消微博推送 图文 uid` |
174
+ | 查看微博订阅列表 | 查看本Bot所有的微博订阅列表,权限:Bot的Master | `#微博全部订阅列表` |
175
175
  | 查看本群/私聊微博订阅列表 | 查看 本群/私聊 添加的微博订阅列表 | `#微博订阅列表` |
176
176
  | 手动推送微博订阅 | 手动触发定时推送任务,权限:Bot的Master | `#执行微博任务` |
177
177
  | 查看博主信息 | 通过uid查看博主信息 | `#微博博主 uid` |
@@ -192,7 +192,7 @@ https://m.weibo.cn/u/7643376782 # 7643376782 为崩坏星穹铁道博主uid
192
192
 
193
193
  # 🧩 五、支持与贡献
194
194
 
195
- 如果你喜欢这个项目,请不妨点个 Star🌟,这是对开发者最大的动力,呜咪~❤️
195
+ 如果你喜欢这个项目,请不妨点个 Star🌟,这是对开发者最大的动力,呜咪~❤️
196
196
 
197
197
  有意见或者建议也欢迎提交 [Issues](https://github.com/snowtafir/yuki-plugin/issues) 和 [Pull requests](https://github.com/snowtafir/yuki-plugin/pulls)。
198
198
 
@@ -8,10 +8,10 @@ pushStatus: 1
8
8
  # "0 5,35,51 * * * *" #每小时固定第5分0秒、第35分0秒、第51分0秒检测一次,共3次/h
9
9
  pushTime: "*/23 * * * *"
10
10
 
11
- # 推送检测间隔,单位为秒,默认7200秒即2小时
11
+ # 推送监测间隔,单位为秒,默认7200秒即2小时,即以当前时间为基准,监测过去2小时内的动态,并推送。取值范围:3600-36000秒,即过去的1-10h。应大于pushTime的周期。
12
12
  interval: 7200
13
13
 
14
- # 是否推送转发的动态 1 - 推送 0 - 不推送
14
+ # 全部订阅的转发动态是否推送: 默认 1 - 开启推送, 0 - 关闭推送。 如果仅仅需要关闭单个订阅的转发动态推送,使用分类订阅指令不包含 转发 分类即可,无需修改此配置。
15
15
  pushTransmit: 1
16
16
 
17
17
  # 推送动态时,限制发送多少张图片
@@ -8,10 +8,10 @@ pushStatus: 1
8
8
  # "0 5,35,51 * * * *" #每小时固定第5分0秒、第35分0秒、第51分0秒检测一次,共3次/h
9
9
  pushTime: "*/23 * * * *"
10
10
 
11
- # 推送检测间隔,单位为秒,默认7200秒即2小时
11
+ # 推送监测间隔,单位为秒,默认7200秒即2小时,即以当前时间为基准,监测过去2小时内的动态,并推送。取值范围:3600-36000秒,即过去的1-10h。应大于pushTime的周期。
12
12
  interval: 7200
13
13
 
14
- # 是否推送转发的动态 1 - 推送 0 - 不推送
14
+ # 全部订阅的转发动态是否推送: 默认 1 - 开启推送, 0 - 关闭推送。 如果仅仅需要关闭单个订阅的转发动态推送,使用分类订阅指令不包含 转发 分类即可,无需修改此配置。
15
15
  pushTransmit: 1
16
16
 
17
17
  # 推送动态时,限制发送多少张图片
package/lib/apps/help.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Messages, Segment } from 'yunzai';
2
- import Image from '../utils/image.js';
2
+ import { renderPage } from '../utils/image.js';
3
3
  import Help from '../models/help/help.js';
4
4
 
5
5
  const message = new Messages('message');
@@ -26,7 +26,7 @@ message.use(async (e) => {
26
26
  isSplit: false,
27
27
  modelName: 'yukiHelp',
28
28
  };
29
- const helpImg = await Image.renderPage("help", "Help", renderData, ScreenshotOptionsData);
29
+ const helpImg = await renderPage("help", "Help", renderData, ScreenshotOptionsData);
30
30
  let imgRes;
31
31
  if (helpImg !== false) {
32
32
  const { img } = helpImg;
@@ -1,5 +1,5 @@
1
1
  import { Messages, Segment } from 'yunzai';
2
- import Image from '../utils/image.js';
2
+ import { renderPage } from '../utils/image.js';
3
3
  import VersionData from '../models/version/version.js';
4
4
 
5
5
  const message = new Messages('message');
@@ -23,7 +23,7 @@ message.use(async (e) => {
23
23
  isSplit: false,
24
24
  modelName: 'yukiVersion',
25
25
  };
26
- const helpImg = await Image.renderPage("version", "Version", renderData, ScreenshotOptionsData);
26
+ const helpImg = await renderPage("version", "Version", renderData, ScreenshotOptionsData);
27
27
  let imgRes;
28
28
  if (helpImg !== false) {
29
29
  const { img } = helpImg;
@@ -7,7 +7,7 @@ import path from 'path';
7
7
  import QRCode from 'qrcode';
8
8
  import YAML from 'yaml';
9
9
  import { Segment, Bot, Redis } from 'yunzai';
10
- import Image from '../../utils/image.js';
10
+ import { renderPage } from '../../utils/image.js';
11
11
  import { _paths } from '../../utils/paths.js';
12
12
  import { BiliApi } from './bilibili.api.js';
13
13
 
@@ -36,7 +36,7 @@ async function applyLoginQRCode(e) {
36
36
  saveHtmlfile: false,
37
37
  modelName: "bili-login"
38
38
  };
39
- const qrCodeImage = await Image.renderPage("bili-login", "LoginQrcodePage", LoginPropsData, ScreenshotOptionsData);
39
+ const qrCodeImage = await renderPage("bili-login", "LoginQrcodePage", LoginPropsData, ScreenshotOptionsData);
40
40
  let qrcodeImg;
41
41
  if (qrCodeImage !== false) {
42
42
  const { img } = qrCodeImage;
@@ -1,7 +1,7 @@
1
1
  import QRCode from 'qrcode';
2
2
  import { Redis, Bot, Segment } from 'yunzai';
3
3
  import Config from '../../utils/config.js';
4
- import Image from '../../utils/image.js';
4
+ import { renderPage } from '../../utils/image.js';
5
5
  import { BiliGetWebData } from './bilibili.get.web.data.js';
6
6
  import { readSyncCookie, postGateway } from './bilibili.models.js';
7
7
  import { BiliQuery } from './bilibili.query.js';
@@ -278,7 +278,7 @@ class BiliTask {
278
278
  * @returns 图片数据
279
279
  */
280
280
  async renderDynamicCard(uid, renderData, ScreenshotOptionsData) {
281
- const dynamicMsg = await Image.renderPage(uid, "MainPage", renderData, ScreenshotOptionsData); // 渲染动态卡片
281
+ const dynamicMsg = await renderPage(uid, "MainPage", renderData, ScreenshotOptionsData); // 渲染动态卡片
282
282
  if (dynamicMsg !== false) {
283
283
  return dynamicMsg.img; // 缓存图片数据
284
284
  }
@@ -1,7 +1,7 @@
1
1
  import QRCode from 'qrcode';
2
2
  import { Redis, Bot, Segment } from 'yunzai';
3
3
  import Config from '../../utils/config.js';
4
- import Image from '../../utils/image.js';
4
+ import { renderPage } from '../../utils/image.js';
5
5
  import { WeiboGetWebData } from './weibo.get.web.data.js';
6
6
  import { WeiboQuery } from './weibo.query.js';
7
7
 
@@ -152,7 +152,7 @@ class WeiboTask {
152
152
  if (!imgs)
153
153
  return;
154
154
  Redis.set(`${markKey}${chatId}:${id_str}`, "1", { EX: 3600 * 10 }); // 设置已发送标记
155
- (logger ?? Bot.logger)?.mark("优纪插件:B站动态执行推送");
155
+ (logger ?? Bot.logger)?.mark("优纪插件:微博动态执行推送");
156
156
  for (let i = 0; i < imgs.length; i++) {
157
157
  const image = imgs[i];
158
158
  await this.sendMessage(chatId, bot_id, chatType, Segment.image(image));
@@ -244,7 +244,7 @@ class WeiboTask {
244
244
  * @returns 图片数据
245
245
  */
246
246
  async renderDynamicCard(uid, renderData, ScreenshotOptionsData) {
247
- const dynamicMsg = await Image.renderPage(uid, "MainPage", renderData, ScreenshotOptionsData); // 渲染动态卡片
247
+ const dynamicMsg = await renderPage(uid, "MainPage", renderData, ScreenshotOptionsData); // 渲染动态卡片
248
248
  if (dynamicMsg !== false) {
249
249
  return dynamicMsg.img; // 缓存图片数据
250
250
  }
@@ -1,22 +1,15 @@
1
- import { Picture, ComponentCreateOpsionType } from 'react-puppeteer';
1
+ import { ComponentCreateOpsionType } from 'react-puppeteer';
2
2
  import { ScreenshotOptions } from '@/utils/puppeteer.render';
3
- export declare class Image extends Picture {
4
- private yukiPuppeteerRender;
5
- /**
6
- * 整合截图方法
7
- */
8
- constructor();
9
- /**
10
- * @param uid 唯一标识符
11
- * @param page 组件名称
12
- * @param props 传入的组件参数
13
- * @param ComponentCreateOpsion 组件创建选项
14
- * @param ScreenshotOptions 截图选项
15
- * @returns {false | {img: buffer[]}}
16
- */
17
- renderPage<T = any>(uid: number | string, page: string, props?: T, ScreenshotOptions?: ScreenshotOptions, ComponentCreateOpsion?: ComponentCreateOpsionType): Promise<false | {
18
- img: Buffer[];
19
- }>;
20
- }
21
- declare const _default: Image;
22
- export default _default;
3
+ /**
4
+ * 渲染列队中的任务
5
+ * @param uid 唯一标识符
6
+ * @param page 组件名称
7
+ * @param props 传入的组件参数
8
+ * @param ComponentCreateOpsion 组件创建选项
9
+ * @param ScreenshotOptions 截图选项
10
+ * @returns {false | {img: buffer[]}}
11
+ */
12
+ declare const renderPage: <T = any>(uid: number | string, page: string, props?: T, ScreenshotOptions?: ScreenshotOptions, ComponentCreateOpsion?: ComponentCreateOpsionType) => Promise<false | {
13
+ img: Buffer[];
14
+ }>;
15
+ export { renderPage };
@@ -3,30 +3,36 @@ import { Picture } from 'react-puppeteer';
3
3
  import { YukiPuppeteerRender } from './puppeteer.render.js';
4
4
  import * as index from '../components/index.js';
5
5
 
6
+ // 定义 Image 类,继承自 Picture 类
6
7
  class Image extends Picture {
8
+ // 私有属性,用于存储 YukiPuppeteerRender 实例
7
9
  yukiPuppeteerRender;
8
10
  /**
9
- * 整合截图方法
11
+ * 构造函数,整合截图方法
10
12
  */
11
13
  constructor() {
12
- // 继承实例
14
+ // 继承父类实例
13
15
  super();
14
16
  // 组件渲染对象
15
17
  this.Com;
16
- // start puppeteer
18
+ // 启动 Puppeteer
17
19
  this.Pup.start();
20
+ // 初始化 YukiPuppeteerRender 实例
18
21
  this.yukiPuppeteerRender = new YukiPuppeteerRender(this.Pup);
19
22
  }
20
23
  /**
21
- * @param uid 唯一标识符
22
- * @param page 组件名称
23
- * @param props 传入的组件参数
24
- * @param ComponentCreateOpsion 组件创建选项
25
- * @param ScreenshotOptions 截图选项
26
- * @returns {false | {img: buffer[]}}
27
- */
28
- async renderPage(uid, page, props = {}, ScreenshotOptions, ComponentCreateOpsion) {
24
+ * 实例方法,用于执行实际的渲染和截图操作
25
+ * @param uid 唯一标识符
26
+ * @param page 组件名称
27
+ * @param props 传入的组件参数
28
+ * @param ComponentCreateOpsion 组件创建选项
29
+ * @param ScreenshotOptions 截图选项
30
+ * @returns {false | {img: buffer[]}}
31
+ */
32
+ async _renderPage(uid, page, props = {}, ScreenshotOptions, ComponentCreateOpsion) {
33
+ // 根据组件名称获取对应的 React 组件
29
34
  const Page = index[page];
35
+ // 调用 yukiPuppeteerRender 进行截图操作
30
36
  return this.yukiPuppeteerRender.yukiScreenshot(this.Com.compile({
31
37
  join_dir: page,
32
38
  html_name: `${uid}.html`,
@@ -35,7 +41,60 @@ class Image extends Picture {
35
41
  }), ScreenshotOptions);
36
42
  }
37
43
  }
38
- // 初始化 图片生成对象
39
- var Image$1 = new Image();
44
+ // 存储单例实例
45
+ let instance = null;
46
+ // 存储任务队列
47
+ const queue = [];
48
+ // 标记当前是否有任务正在处理
49
+ let isProcessing = false;
50
+ /**
51
+ * 处理队列中的任务
52
+ */
53
+ const processQueue = async () => {
54
+ // 如果队列为空,设置 isProcessing 为 false 并返回
55
+ if (queue.length === 0) {
56
+ isProcessing = false;
57
+ return;
58
+ }
59
+ // 设置 isProcessing 为 true,表示有任务正在处理
60
+ isProcessing = true;
61
+ // 从队列中取出第一个任务
62
+ const { uid, page, props, ScreenshotOptions, ComponentCreateOpsion, resolve, reject } = queue.shift();
63
+ try {
64
+ // 调用实例方法 renderPage 执行任务
65
+ const img = await instance._renderPage(uid, page, props, ScreenshotOptions, ComponentCreateOpsion);
66
+ // 任务成功完成,调用 resolve 回调函数
67
+ resolve(img);
68
+ }
69
+ catch (error) {
70
+ // 任务失败,打印错误信息并调用 reject 回调函数
71
+ console.error(error);
72
+ reject(false);
73
+ }
74
+ // 处理下一个任务
75
+ processQueue();
76
+ };
77
+ /**
78
+ * 渲染列队中的任务
79
+ * @param uid 唯一标识符
80
+ * @param page 组件名称
81
+ * @param props 传入的组件参数
82
+ * @param ComponentCreateOpsion 组件创建选项
83
+ * @param ScreenshotOptions 截图选项
84
+ * @returns {false | {img: buffer[]}}
85
+ */
86
+ const renderPage = async (uid, page, props = {}, ScreenshotOptions, ComponentCreateOpsion) => {
87
+ if (!instance) {
88
+ instance = new Image();
89
+ }
90
+ return new Promise((resolve, reject) => {
91
+ // 将任务添加到队列中
92
+ queue.push({ uid, page, props, ScreenshotOptions, ComponentCreateOpsion, resolve, reject });
93
+ // 如果没有任务正在处理,则开始处理队列
94
+ if (!isProcessing) {
95
+ processQueue();
96
+ }
97
+ });
98
+ };
40
99
 
41
- export { Image, Image$1 as default };
100
+ export { renderPage };
@@ -49,20 +49,13 @@ class YukiPuppeteerRender {
49
49
  const boundingBox = await element.boundingBox(); // 获取内容区域的边界框信息
50
50
  const num = Options?.isSplit ? Math.ceil(boundingBox.height / pageHeight) : 1; // 根据是否需要分片,计算分片数量,默认为 1
51
51
  pageHeight = Math.round(boundingBox.height / num); //动态调整分片高度,防止过短影响观感。
52
- await page.setViewport({
53
- width: boundingBox.width + 50,
54
- height: pageHeight + 100
55
- });
52
+ await page.setViewport({ width: boundingBox.width + 50, height: pageHeight + 100 });
56
53
  // 根据 style 的值来修改 CSS 样式
57
54
  if (Options?.addStyle) {
58
- await page.addStyleTag({
59
- content: Options.addStyle,
60
- });
55
+ await page.addStyleTag({ content: Options.addStyle, });
61
56
  }
62
57
  // 禁止 GIF 动图播放
63
- await page.addStyleTag({
64
- content: `img[src$=".gif"] {animation-play-state: paused !important;}`
65
- });
58
+ await page.addStyleTag({ content: `img[src$=".gif"] {animation-play-state: paused !important;}` });
66
59
  // 是否保存 html 文件
67
60
  if (Options?.saveHtmlfile === true) {
68
61
  const htmlContent = await page.content();
@@ -73,15 +66,11 @@ class YukiPuppeteerRender {
73
66
  fs__default.writeFileSync(`${Dir}${Date.now()}.html`, htmlContent);
74
67
  }
75
68
  logger.info('[puppeteer] success');
76
- let numSun = 0;
77
69
  let start = Date.now();
78
70
  const ret = new Array();
79
- let buff;
80
71
  for (let i = 1; i <= num; i++) {
81
72
  if (i > 1) {
82
- await page.evaluate(pageHeight => {
83
- window.scrollBy(0, pageHeight); // 在页面上下文中执行滚动操作
84
- }, pageHeight);
73
+ await page.evaluate(pageHeight => { window.scrollBy(0, pageHeight); }, pageHeight); // 在页面上下文中执行滚动操作
85
74
  await new Promise((resolve) => setTimeout(resolve, 500)); // 等待一段时间,确保页面加载完成
86
75
  }
87
76
  let renderOptions = Options?.SOptions ?? { type: 'png' };
@@ -94,16 +83,15 @@ class YukiPuppeteerRender {
94
83
  height: Math.min(pageHeight, boundingBox.height - pageHeight * (i - 1)), // 截图区域的高度取决于内容区域剩余的高度或者默认的分片高度
95
84
  },
96
85
  };
97
- buff = await element.screenshot(screenshotOptions).catch(err => {
86
+ const buff = await element.screenshot(screenshotOptions).catch(err => {
98
87
  logger.error('[puppeteer]', 'screenshot', err);
99
88
  return false;
100
89
  }); // 对指定区域进行截图
101
- numSun++; // 增加截图次数
102
90
  if (buff !== false) {
103
- let imgBuff = !Buffer.isBuffer(buff) ? Buffer.from(buff) : buff;
91
+ const imgBuff = !Buffer.isBuffer(buff) ? Buffer.from(buff) : buff;
104
92
  /** 计算图片大小 */
105
93
  const kb = (imgBuff?.length / 1024).toFixed(2) + "kb"; // 计算图片大小
106
- logger.mark(`[图片生成][${name}][${numSun}次] ${kb} ${logger.green(`${Date.now() - start}ms`)}`); // 记录日志
94
+ logger.mark(`[图片生成][${name}][${i}次] ${kb} ${logger.green(`${Date.now() - start}ms`)}`); // 记录日志
107
95
  ret.push(imgBuff); // 将截图结果添加到数组中
108
96
  }
109
97
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yz-yuki-plugin",
3
- "version": "2.0.3-7",
3
+ "version": "2.0.4-1",
4
4
  "description": "优纪插件,yunzaijs 关于 微博推送、B站推送 等功能的拓展插件",
5
5
  "author": "snowtafir",
6
6
  "type": "module",
@@ -34,7 +34,7 @@
34
34
  "qrcode": "^1.5.4",
35
35
  "react": "^18.3.1",
36
36
  "react-dom": "^18.3.1",
37
- "react-puppeteer": "^1.0.7-rc.0",
37
+ "react-puppeteer": "^1.0.7",
38
38
  "redis": "^4.7.0",
39
39
  "yaml": "^2.5.1",
40
40
  "yarn": "^1.19.1"
@@ -76,7 +76,7 @@
76
76
  "qrcode": "^1.5.4",
77
77
  "react": "^18.3.1",
78
78
  "react-dom": "^18.3.1",
79
- "react-puppeteer": "^1.0.7-rc.0",
79
+ "react-puppeteer": "^1.0.7",
80
80
  "redis": "^4.7.0",
81
81
  "rollup": "^4.20.0",
82
82
  "rollup-plugin-auto-external": "^2.0.0",
package/public/output.css CHANGED
@@ -1 +1 @@
1
- /*! tailwindcss v3.4.10 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.m-auto{margin:auto}.mb-3{margin-bottom:.75rem}.ml-7{margin-left:1.75rem}.mt-3{margin-top:.75rem}.h-10{height:2.5rem}.h-12{height:3rem}.h-72{height:18rem}.max-h-96{max-height:24rem}.w-32{width:8rem}.w-72{width:18rem}.w-96{width:24rem}.p-1{padding:.25rem}.p-5{padding:1.25rem}.text-center{text-align:center}.text-lg{font-size:1.125rem;line-height:1.75rem}.italic{font-style:italic}.text-blue-500{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity))}.text-red-600{--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity))}
1
+ /*! tailwindcss v3.4.11 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.m-auto{margin:auto}.mb-3{margin-bottom:.75rem}.ml-7{margin-left:1.75rem}.mt-3{margin-top:.75rem}.h-10{height:2.5rem}.h-12{height:3rem}.h-72{height:18rem}.max-h-96{max-height:24rem}.w-32{width:8rem}.w-72{width:18rem}.w-96{width:24rem}.p-1{padding:.25rem}.p-5{padding:1.25rem}.text-center{text-align:center}.text-lg{font-size:1.125rem;line-height:1.75rem}.italic{font-style:italic}.text-blue-500{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity))}.text-red-600{--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity))}