Full Code of P1kaj1uu/VIP-Video-Parsing for AI

master 0f2749e2d000 cached
285 files
2.4 MB
643.9k tokens
919 symbols
1 requests
Download .txt
Showing preview only (2,645K chars total). Download the full file or copy to clipboard to get everything.
Repository: P1kaj1uu/VIP-Video-Parsing
Branch: master
Commit: 0f2749e2d000
Files: 285
Total size: 2.4 MB

Directory structure:
gitextract_dr35u3vo/

├── .browserslistrc
├── .dockerignore
├── .gitignore
├── Dockerfile
├── LICENSE
├── README.md
├── api/
│   └── index.ts
├── deploy.sh
├── docker-compose.yml
├── email.config.d.ts
├── email.config.js
├── index.html
├── jsconfig.json
├── package.json
├── postcss.config.js
├── public/
│   ├── .htaccess
│   ├── index.html
│   ├── layui/
│   │   ├── css/
│   │   │   ├── layui.css
│   │   │   ├── layui.mobile.css
│   │   │   └── modules/
│   │   │       ├── code.css
│   │   │       ├── laydate/
│   │   │       │   └── default/
│   │   │       │       └── laydate.css
│   │   │       └── layer/
│   │   │           └── default/
│   │   │               └── layer.css
│   │   ├── lay/
│   │   │   └── modules/
│   │   │       ├── carousel.js
│   │   │       ├── code.js
│   │   │       ├── element.js
│   │   │       ├── flow.js
│   │   │       ├── form.js
│   │   │       ├── jquery.js
│   │   │       ├── laydate.js
│   │   │       ├── layedit.js
│   │   │       ├── layer.js
│   │   │       ├── laypage.js
│   │   │       ├── laytpl.js
│   │   │       ├── mobile.js
│   │   │       ├── rate.js
│   │   │       ├── table.js
│   │   │       ├── tree.js
│   │   │       ├── upload.js
│   │   │       └── util.js
│   │   ├── layui.all.js
│   │   └── layui.js
│   ├── model/
│   │   └── heartBeat.obj
│   └── three/
│       ├── MeshSurfaceSampler.js
│       ├── OBJLoader.js
│       ├── TrackballControls.js
│       └── simplex-noise.js
├── python_backend/
│   ├── app/
│   │   ├── api/
│   │   │   ├── routers.py
│   │   │   └── v1/
│   │   │       ├── interpreter.py
│   │   │       └── ollama.py
│   │   ├── common/
│   │   │   ├── log.py
│   │   │   ├── open_interpreter.py
│   │   │   └── response.py
│   │   ├── core/
│   │   │   ├── conf.py
│   │   │   └── path_conf.py
│   │   ├── main.py
│   │   ├── requirements.txt
│   │   └── schemas/
│   │       ├── interpreter.py
│   │       └── ollama.py
│   ├── config/
│   │   ├── config.py
│   │   └── config_private.py
│   ├── content.txt
│   ├── main.py
│   ├── requirements.txt
│   └── tools/
│       ├── config_tool.py
│       ├── generate_video.py
│       ├── image_effect.py
│       ├── read_file.py
│       └── stable_api.py
├── src/
│   ├── App.tsx
│   ├── assets/
│   │   └── css/
│   │       ├── ai.scss
│   │       ├── global.css
│   │       ├── gold.scss
│   │       └── markdown.css
│   ├── components/
│   │   ├── ClickEffect.tsx
│   │   ├── Footer.tsx
│   │   ├── HeartBeat.tsx
│   │   ├── Live2DDashboard.tsx
│   │   ├── Navbar.tsx
│   │   ├── VerifyCode.tsx
│   │   ├── VersionUpdateModal.tsx
│   │   ├── goofish/
│   │   │   ├── GoofishLayout.tsx
│   │   │   └── index.ts
│   │   ├── latex/
│   │   │   ├── FileTree.tsx
│   │   │   ├── LatexEditor.tsx
│   │   │   ├── PdfPreview.tsx
│   │   │   └── tree.css
│   │   ├── markmap/
│   │   │   ├── EditNodeModal.tsx
│   │   │   ├── EditorPanel.tsx
│   │   │   ├── InfoModal.tsx
│   │   │   ├── LandscapeMode.tsx
│   │   │   ├── MindmapPanel.tsx
│   │   │   ├── MobileMenu.tsx
│   │   │   ├── MobileTabBar.tsx
│   │   │   └── PromptModal.tsx
│   │   └── video/
│   │       ├── VideoDownload.tsx
│   │       └── VideoParse.tsx
│   ├── goofish/
│   │   ├── ai-tools/
│   │   │   ├── chat-history.tool.ts
│   │   │   ├── index.ts
│   │   │   └── order-query.tool.ts
│   │   ├── api/
│   │   │   ├── index.ts
│   │   │   ├── middlewares/
│   │   │   │   ├── index.ts
│   │   │   │   └── security.middleware.ts
│   │   │   ├── routes/
│   │   │   │   ├── accounts.ts
│   │   │   │   ├── auth.route.ts
│   │   │   │   ├── autoreply.ts
│   │   │   │   ├── autosell.ts
│   │   │   │   ├── conversations.ts
│   │   │   │   ├── dev-messages.route.ts
│   │   │   │   ├── goods.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── logs.ts
│   │   │   │   ├── messages.ts
│   │   │   │   ├── order.route.ts
│   │   │   │   ├── status.ts
│   │   │   │   ├── workflow.route.ts
│   │   │   │   └── ws-push.route.ts
│   │   │   ├── server.ts
│   │   │   └── stores/
│   │   │       ├── conversation.store.ts
│   │   │       └── message.store.ts
│   │   ├── core/
│   │   │   ├── constants.ts
│   │   │   ├── cookies.manager.ts
│   │   │   ├── event-emitter.ts
│   │   │   └── logger.ts
│   │   ├── db/
│   │   │   ├── account.repository.ts
│   │   │   ├── autoreply.repository.ts
│   │   │   ├── autosell.repository.ts
│   │   │   ├── connection.ts
│   │   │   ├── conversation.repository.ts
│   │   │   ├── index.ts
│   │   │   ├── migrations.ts
│   │   │   ├── order.repository.ts
│   │   │   ├── settings.repository.ts
│   │   │   ├── user-avatar.repository.ts
│   │   │   ├── user.repository.ts
│   │   │   └── workflow.repository.ts
│   │   ├── index.ts
│   │   ├── services/
│   │   │   ├── ai.service.ts
│   │   │   ├── auth.service.ts
│   │   │   ├── autoreply.service.ts
│   │   │   ├── autosell.service.ts
│   │   │   ├── conversation.service.ts
│   │   │   ├── goods.service.ts
│   │   │   ├── index.ts
│   │   │   ├── message.service.ts
│   │   │   ├── order.service.ts
│   │   │   ├── user.service.ts
│   │   │   └── workflow.service.ts
│   │   ├── types/
│   │   │   ├── account.types.ts
│   │   │   ├── autoreply.types.ts
│   │   │   ├── autosell.types.ts
│   │   │   ├── common.types.ts
│   │   │   ├── conversation.types.ts
│   │   │   ├── goods.types.ts
│   │   │   ├── index.ts
│   │   │   ├── message.types.ts
│   │   │   ├── order.types.ts
│   │   │   ├── user.types.ts
│   │   │   └── workflow.types.ts
│   │   ├── utils/
│   │   │   ├── cookies.ts
│   │   │   ├── crypto.ts
│   │   │   ├── date.ts
│   │   │   ├── index.ts
│   │   │   ├── jwt.util.ts
│   │   │   ├── msgpack.ts
│   │   │   └── password.util.ts
│   │   └── websocket/
│   │       ├── client.manager.ts
│   │       ├── client.ts
│   │       ├── index.ts
│   │       ├── message.parser.ts
│   │       ├── message.receiver.ts
│   │       ├── message.sender.ts
│   │       └── token.ts
│   ├── hooks/
│   │   ├── goofish/
│   │   │   ├── index.ts
│   │   │   └── useWebSocket.ts
│   │   └── useChangeLanguage.ts
│   ├── i18n/
│   │   ├── config.ts
│   │   └── locales/
│   │       ├── en/
│   │       │   ├── about.ts
│   │       │   ├── aimarkmap.ts
│   │       │   ├── cartoon.ts
│   │       │   ├── cartoonChapter.ts
│   │       │   ├── cartoonDetail.ts
│   │       │   ├── common.ts
│   │       │   ├── gold.ts
│   │       │   ├── goofish.ts
│   │       │   ├── gpt.ts
│   │       │   ├── help.ts
│   │       │   ├── home.ts
│   │       │   ├── latex.ts
│   │       │   ├── login.ts
│   │       │   ├── music.ts
│   │       │   ├── nav.ts
│   │       │   ├── notFound.ts
│   │       │   ├── paper.ts
│   │       │   ├── textToPhoto.ts
│   │       │   ├── trans.ts
│   │       │   ├── versionUpdate.ts
│   │       │   └── video.ts
│   │       ├── en.ts
│   │       ├── zh/
│   │       │   ├── about.ts
│   │       │   ├── aimarkmap.ts
│   │       │   ├── cartoon.ts
│   │       │   ├── cartoonChapter.ts
│   │       │   ├── cartoonDetail.ts
│   │       │   ├── common.ts
│   │       │   ├── gold.ts
│   │       │   ├── goofish.ts
│   │       │   ├── gpt.ts
│   │       │   ├── help.ts
│   │       │   ├── home.ts
│   │       │   ├── latex.ts
│   │       │   ├── login.ts
│   │       │   ├── music.ts
│   │       │   ├── nav.ts
│   │       │   ├── notFound.ts
│   │       │   ├── paper.ts
│   │       │   ├── textToPhoto.ts
│   │       │   ├── trans.ts
│   │       │   ├── versionUpdate.ts
│   │       │   └── video.ts
│   │       └── zh.ts
│   ├── index.css
│   ├── main.tsx
│   ├── pages/
│   │   ├── About.tsx
│   │   ├── Cartoon.tsx
│   │   ├── CartoonChapter.tsx
│   │   ├── CartoonDetail.tsx
│   │   ├── GPT.tsx
│   │   ├── Gold.tsx
│   │   ├── Help.tsx
│   │   ├── Home.tsx
│   │   ├── Latex.tsx
│   │   ├── Login.tsx
│   │   ├── Markmap.tsx
│   │   ├── Music.tsx
│   │   ├── NotFound.tsx
│   │   ├── PaperListPage.tsx
│   │   ├── TextToPhoto.tsx
│   │   ├── Trans.tsx
│   │   ├── Video.tsx
│   │   ├── WorkerAgent.tsx
│   │   └── goofish/
│   │       ├── Accounts/
│   │       │   └── index.tsx
│   │       ├── AutoReply/
│   │       │   └── index.tsx
│   │       ├── AutoSell/
│   │       │   └── index.tsx
│   │       ├── Conversations/
│   │       │   └── index.tsx
│   │       ├── Dashboard/
│   │       │   └── index.tsx
│   │       ├── Goods/
│   │       │   └── index.tsx
│   │       ├── Logs/
│   │       │   └── index.tsx
│   │       ├── Orders/
│   │       │   └── index.tsx
│   │       └── Workflow/
│   │           └── index.tsx
│   ├── sdk/
│   │   ├── analytics/
│   │   │   ├── README.md
│   │   │   ├── examples.tsx
│   │   │   ├── hooks.ts
│   │   │   ├── index.ts
│   │   │   └── types.d.ts
│   │   ├── fingerprint/
│   │   │   ├── README.md
│   │   │   ├── collector.ts
│   │   │   ├── examples.tsx
│   │   │   ├── hash.ts
│   │   │   ├── hooks.ts
│   │   │   ├── index.ts
│   │   │   ├── storage.ts
│   │   │   └── types.ts
│   │   └── index.ts
│   ├── services/
│   │   ├── goofish/
│   │   │   └── index.ts
│   │   ├── latexApi.ts
│   │   ├── server.ts
│   │   └── turnstile.ts
│   ├── types/
│   │   ├── goofish/
│   │   │   └── index.ts
│   │   └── turnstile.d.ts
│   ├── utils/
│   │   ├── backTop.ts
│   │   ├── checkIp.ts
│   │   ├── images.ts
│   │   ├── isMobile.ts
│   │   ├── markmap.ts
│   │   ├── paper.ts
│   │   ├── price.ts
│   │   ├── token.ts
│   │   └── versionChecker.ts
│   ├── version.d.ts
│   └── vite-env.d.ts
├── tailwind.config.js
├── tsconfig.json
├── tsconfig.node.json
├── vercel.json
└── vite.config.ts

================================================
FILE CONTENTS
================================================

================================================
FILE: .browserslistrc
================================================
> 1%
last 2 versions
not dead


================================================
FILE: .dockerignore
================================================
# 依赖
node_modules
npm-debug.log
yarn-error.log
yarn-debug.log
package-lock.json

# 构建产物
dist
build

# 环境变量
.env.local
.env.*.local

# IDE
.vscode
.idea
*.swp
*.swo
.DS_Store

# Git
.git
.gitignore
.gitattributes

# 文档
README.md
*.md

# 测试
coverage
.nyc_output

# 其他
*.log
temp
tmp


================================================
FILE: .gitignore
================================================
.DS_Store
node_modules
/dist

# 闲鱼日志文件
/logs
# 闲鱼数据库文件
/data


# local env files - ignore all .env files except .env.example
.env
.env.local
.env.*.local
.env.development
.env.production
.env.test

# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*

# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?


================================================
FILE: Dockerfile
================================================
# 多阶段构建 - 构建阶段
FROM node:20-alpine AS builder

# 设置工作目录
WORKDIR /app

# 安装 yarn
RUN corepack enable && corepack prepare yarn@stable --activate

# 复制 package.json 和 yarn.lock
COPY package.json yarn.lock ./

# 安装依赖
RUN yarn install --frozen-lockfile

# 复制源代码
COPY . .

# 构建生产环境代码
RUN yarn build

# 生产阶段 - 使用 nginx 托管静态文件
FROM nginx:alpine

# 复制 nginx 配置
COPY nginx.conf /etc/nginx/conf.d/default.conf

# 从构建阶段复制构建产物
COPY --from=builder /app/dist /usr/share/nginx/html

# 暴露端口
EXPOSE 80

# 启动 nginx
CMD ["nginx", "-g", "daemon off;"]


================================================
FILE: LICENSE
================================================
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: README.md
================================================
<h1 align="center">
 <p>ChattyPlay-Agent</p>
 <a href="https://trendshift.io/repositories/20110" target="_blank"><img src="https://trendshift.io/api/badge/repositories/13630" alt="ChattyPlay-Agent | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
 <p>
  <img alt="Github Stargazers" src="https://img.shields.io/github/stars/P1Kaj1uu/ChattyPlay-Agent.svg?label=%e6%98%9f%e6%a0%87&logo=github&logoColor=white&labelColor=black&color=gold&style=for-the-badge&cacheSeconds=10">
  <img alt="Github Forks" src="https://img.shields.io/github/forks/P1Kaj1uu/ChattyPlay-Agent?label=%e5%a4%8d%e5%88%bb&logo=github&logoColor=white&labelColor=black&color=grey&style=for-the-badge&cacheSeconds=10">
   <img alt="Github Licence" src="https://img.shields.io/github/license/P1Kaj1uu/ChattyPlay-Agent?label=%e8%ae%b8%e5%8f%af&logo=github&logoColor=white&labelColor=black&color=grey&style=for-the-badge&cacheSeconds=10">
  <br />
  <img src="https://img.shields.io/badge/支持平台-Windows_|_Mac_|_Linux_|_iPhone_|_Android-blueviolet.svg?style=for-the-badge" alt="支持平台">
 </p>
</h1>

## 📄 免责声明

### 本项目提供音乐、影视解析下载、实时黄金及K线图、动漫漫画、Hugging Face论文、思维导图、闲鱼助手和ChatGPT相关服务,仅供学习使用,请勿用于任何商业用途。如你有更好的想法、建议、或不解的问题,欢迎提PR或Issues!如有侵权,请联系我!

> License:ChattyPlay-Agent is licensed under the Apache-2.0 License. See the [LICENSE](https://github.com/P1kaj1uu/ChattyPlay-Agent/blob/master/LICENSE) file for more information.

> 项目描述参考信息可跳转WiKi:https://github.com/P1kaj1uu/ChattyPlay-Agent/wiki/%E4%BD%BF%E7%94%A8%E6%96%87%E6%A1%A3

> PC端、移动端均已适配

## 🚀 在线体验

- 体验地址:<a href="https://chatty-play-agent.vercel.app" target="_blank">在线体验</a>(需要科学上网工具访问)
- 视频预览:<a href="https://www.bilibili.com/video/BV1DmFYzbEQp/?share_source=copy_web&vd_source=1c9f57ed1dd7f17c0142ea7c34926f1e" target="_blank">录频视频</a>
- 备注:如使用ChatGPT服务,我的APIKey配额有限,希望大家能省点用谢谢!

## 📖 版本迭代

- v1.0版本已完成(2023.1.7凌晨)
- v1.1版本优化观看页面的提示内容(2023.1.7上午)
- v1.2版本优化解析接口(2023.1.8下午)
- v1.3版本优化帮助内容页面(2023.1.8下午)
- v1.4版本优化分离加载爱心跳动效果(2023.1.9上午)
- v1.5版本增加多个能用的视频解析接口(2023.1.14晚上)
- v1.6版本禁止F12查看源代码(2023.1.19上午)
- v1.7版本增加并优化解析视频接口(2023.1.19下午)
- v1.8版本浏览器兼容判断浏览器类型(2023.1.20晚上)
- v2.0版本优化页面样式,增加首页听音乐功能(2023.4.22下午)
- v2.1版本增加论文降重功能(2023.4.30全天)
- v2.2版本接入ChatGPT服务,可无需再代理和APIkey(2023.5.1-2023.5.3)
- v2.3版本优化ChatGPT服务,检测自动换行,并支持上下文对话(2023.5.6-2023.5.7)
- v2.4版本代仓本地的部分接口隐藏不对外开放(2023.5.7晚上)
- v2.5版本输出代码高亮显示,流式处理EventStream,并支持会话存储(2023.5.13-2023.5.14)
- v2.6版本接入文心一言基础服务,增加语音聊天、语音朗读功能(2023.5.20-2023.5.21)
- v2.7版本前端也做限流处理,增加验证功能,防止接口被恶意多次请求(2023.5.24晚上)
- v2.8版本优化加载效果,增加网站访问次数统计和版本更新提醒用户功能(2023.5.26-2023.5.27)
- v2.9版本整体优化代码,修复bug,并抽离封装部分函数和组件,降低复杂度,实现高内聚低耦合(2023.6.10-2023.6.20)
- v3.0全新版本上线,优化markdown代码块格式,并接入文生图、亚马逊爬虫服务,发布浏览器插件(2023.7.24-2023.8.13)
- v4.0重构项目完成,修改相关的bug,页面结构样式重新设计,增加实时黄金及k线图、动漫漫画和Hugging Face论文功能,优化用户体验,并完成移动端和PC端的适配,添加版本检测弹窗更新功能,接入SDK、MCP、Agent、谷歌和Github授权登录等服务,同时系统整体架构将Vue2替换为React + TypeScript + Hono + Vite + Tailwind CSS + i18n国际化 + live2d看板娘 + nginx + Docker容器化管理(2025.12.16-2026.2.10)
- v4.1闲鱼助手,可自动回复、自动发货等(2026.2.26-2026.3.7)
- v4.2思维导图和Markdown格式预览及编辑,导出图片。论文PDF预览及AI问答(2026.3.9-2026.3.11)
- v4.3解析下载无水印1080P视频(2026.3.13-2026.3.14)
- v4.4封装浏览器指纹SDK、GIF图片埋点SDK,接入Google Analytics分析工具(2026.3.15-2026.3.16)
- v4.5优化注册登录功能,jwt token无感刷新token,redis存储用户信息,cloudflare托管vercel(2026.3.22-2026.3.26)
- v4.6新增hcaptcha防机器人验证码(2026.4.12)
- v4.7新增cloudflare turnstile防恶意请求和爬虫(2026.4.25-2026.4.27)
- v4.8新增支付功能(2026.4.28-2026.4.29)
- v4.9新增Latex编辑功能(2026.5.2-2026.5.8)

## 最新版本V4.9(推荐)

> 本地调试时,可注释掉限制调用控制台的代码。参照说明修改package.json、email.config.js、index.html、.env.development、.env.production和docker-compose.yml文件。

## 🛠️ 系统架构

<img width="1345" height="750" alt="Image" src="https://github.com/user-attachments/assets/c52fe4e9-a04c-4712-b635-a7191712cbf4" />
<img width="1345" height="750" alt="Image" src="https://github.com/user-attachments/assets/30aa8602-e8c0-4e0c-8165-0a4061f39d64" />

## 🔰 项目概述

✅ 技术栈

- Python + React + TypeScript + Vite + Tailwind CSS + i18n国际化 + live2d看板娘
- 适配移动端和PC端
- Three.js 3D 模型加载动画效果
- MD5加密,验证码,网站访问次数统计
- Markdown语法解析,highlight代码高亮显示
- 处理EventStream流
- 金融基金K线图
- 实时版本检测更新
- 限制终端控制台调用
- 组件库使用Antd
- 接入fundebug SDK和OPen AI SDK
- Hugging Face今日论文和热搜论文、预览PDF及对PDF的AI解答
- 接入MCP、Agent相关服务
- 接入谷歌和Github授权登录
- 闲鱼助手,可自动回复、自动发货等
- 思维导图和Markdown格式预览及编辑,导出图片
- 解析下载无水印1080P视频
- 封装浏览器指纹SDK、GIF图片埋点SDK
- 接入Google Analytics分析工具
- jwt token无感刷新token
- redis存储用户信息
- cloudflare托管vercel
- hcaptcha防机器人验证码
- cloudflare turnstile防恶意请求和爬虫
- 支付宝支付功能
- Latex编辑,可替代overleaf使用

✅ 音乐播放

- 无需登录,可快速上手使用
- 支持歌曲/歌手的模糊搜索,播放歌曲和查看热评
- 支持歌曲倍速播放

✅ 视频解析

- 无需会员,可在线解析视频,解析速度快
- 包含海量视频资源,提供多个可用的解析接口
- 支持全屏、倍速播放,画质超清及以上
- 解析下载无水印1080P视频

✅ 实时黄金

- 页面水印,版权认证
- 实时获取黄内金价和国外金价数据
- TradingView实时k线图数据波动
- 支持切换日期范围选择、复制图片、下载图片

✅ 论文文献

- Hugging Face今日论文和热搜论文
- 筛选论文,项目主页,代码地址,arXiv论文等
- PC端和移动端适配预览论文PDF
- 对论文PDF进行AI解答

✅ Latex编辑

- 支持overleaf的90%功能,编译不受限制
- 编辑本地保存,不会上传到服务器,信息安全
- 支持导出PDF文件
- 支持自定义模板

✅ 思维导图

- 生成对应内容的思维导图和Markdown内容,可导出SVG图片和PNG图片
- 支持拖拽、放大、缩小、全屏
- 适配PC端和移动端

✅ ChatGPT

- 集成OpenAi API (DeepSeek V3.2模型),无需再代理,可快速使用
- 支持markdown格式,代码高亮,代码复制,公式和图表展示
- 无限轮聊天 + 带上下文逻辑
- 流式输出,可中断输出,实时会话存储管理,导出聊天记录
- 语音聊天 + 语音播放

✅ 文生图

- 接入文生图模型(MidJourney / Stable Diffusion Model)
- 支持大量多语言的AI绘图
- 提供20+种生成的图片风格

✅ 动漫漫画

- 有13个热门榜单,上万本图漫,包括人气榜、新作榜、畅销榜、日漫榜、恋爱榜、剧情榜、投稿榜、完结榜、免费榜、等免榜、月票榜
- 支持漫画名/漫画作者/漫画内容的模糊搜索,可快速切换漫画上一章、下一章

## ➰ 效果展示

- ### 关于我

<img width="1470" height="792" alt="Image" src="https://github.com/user-attachments/assets/8e34e58f-ea63-47ce-9070-09f63fe6564d" />
<img width="1470" height="792" alt="Image" src="https://github.com/user-attachments/assets/0e293ff8-dedd-47a8-a51f-22f0211e944e" />
<img width="1470" height="793" alt="Image" src="https://github.com/user-attachments/assets/32c04cc5-4a05-4b8e-962b-fbedcf0af228" />

- ### 版本检测

<img width="734" height="674" alt="Image" src="https://github.com/user-attachments/assets/e2bc2706-b137-47bd-b24a-46585f6a9349" />
<img width="1017" height="677" alt="Image" src="https://github.com/user-attachments/assets/464e4219-57ea-46bc-8267-8abbc90bc752" />

- ### 登录页面

<img width="1214" height="763" alt="Image" src="https://github.com/user-attachments/assets/2a55a46d-af30-4122-b417-5425288ef3ac" />
<img width="1312" height="791" alt="Image" src="https://github.com/user-attachments/assets/2714de5f-471c-40f5-af41-dbe4b21a3813" />
<img width="1413" height="708" alt="Image" src="https://github.com/user-attachments/assets/19d1df96-4fad-4c22-b720-10d1deed48b3" />
<img width="1276" height="659" alt="Image" src="https://github.com/user-attachments/assets/9570cfd9-8189-4e5e-b3d0-9f3ab81b888e" />

- ### 爱心跳动

  ![image](https://github.com/P1kaj1uu/VIP-Video-Parsing/assets/94435057/02b9367e-0b6e-4cde-8157-36b5731aa518)

- ### 主页

<img width="1470" height="794" alt="Image" src="https://github.com/user-attachments/assets/e745dfcf-e04d-49bf-b2dc-bb19cf66ded0" />
<img width="1470" height="795" alt="Image" src="https://github.com/user-attachments/assets/82a34ebe-00eb-4c37-8b90-82f53f604a44" />
<img width="667" height="708" alt="Image" src="https://github.com/user-attachments/assets/4711dbd6-4a24-4d28-a47e-db95f6934d3e" />
<img width="663" height="694" alt="Image" src="https://github.com/user-attachments/assets/90a5388e-2008-47d6-afcd-939277aa4086" />

- ### 观看页面

<img width="1470" height="794" alt="Image" src="https://github.com/user-attachments/assets/d085b162-3bd2-4444-9dd7-ada732efd689" />
<img width="1467" height="787" alt="Image" src="https://github.com/user-attachments/assets/65b9e758-818d-4e49-9936-808070ab45c0" />
<img width="1470" height="792" alt="Image" src="https://github.com/user-attachments/assets/2153eb1a-3ae5-4ff9-9583-11329bde77e2" />
<img width="1470" height="789" alt="Image" src="https://github.com/user-attachments/assets/a7bf05d8-f5d3-419c-b0ee-0013e925b00e" />
<img width="1470" height="789" alt="Image" src="https://github.com/user-attachments/assets/54fdfd8b-35c7-4e8e-839e-0df12beb02ef" />
<img width="695" height="776" alt="Image" src="https://github.com/user-attachments/assets/956e3d33-c4b4-471f-b004-6beac7bc32a2" />
<img width="1470" height="795" alt="Image" src="https://github.com/user-attachments/assets/b26084b3-1a26-4b21-8fc5-e38b9ad50540" />
<img width="1470" height="719" alt="Image" src="https://github.com/user-attachments/assets/02f91f09-9d14-4cd3-a101-8d004ef1cebb" />
<img width="493" height="720" alt="Image" src="https://github.com/user-attachments/assets/e4b9f98a-b053-47bc-94a5-32cc615314ce" />

- ### 论文文献

<img width="1470" height="794" alt="Image" src="https://github.com/user-attachments/assets/29a0ba7d-13de-425a-9de3-fe1d272a140f" />
<img width="1470" height="793" alt="Image" src="https://github.com/user-attachments/assets/26c8630a-4329-48d6-bad6-9846a708fcef" />
<img width="1470" height="795" alt="Image" src="https://github.com/user-attachments/assets/8d08a59c-b673-430e-99de-0230ed0db149" />
<img width="675" height="779" alt="Image" src="https://github.com/user-attachments/assets/ec888e37-8bb5-42ba-bbb6-10ef2123fd39" />
<img width="664" height="721" alt="Image" src="https://github.com/user-attachments/assets/0063a312-700c-46a8-ae34-f601b53fecf9" />
<img width="699" height="709" alt="Image" src="https://github.com/user-attachments/assets/ff1eeb84-337e-4b9d-87b7-a37e2ffa4125" />

- ### Latex编辑

<img width="1470" height="797" alt="Image" src="https://github.com/user-attachments/assets/d81e4592-e9b9-4e0e-8e2b-17ea6625fe0e" />
<img width="495" height="724" alt="Image" src="https://github.com/user-attachments/assets/5c55ff60-53e4-40c3-8c1a-2148d78d2a00" />

- ### 思维导图

<img width="1470" height="794" alt="Image" src="https://github.com/user-attachments/assets/0d3008fd-f418-4925-85c1-90d87928f850" />
<img width="1470" height="797" alt="Image" src="https://github.com/user-attachments/assets/fae232f3-1a1d-4092-ad2d-ab9eb99bb79f" />
<img width="495" height="726" alt="Image" src="https://github.com/user-attachments/assets/b6182a74-9197-4fd6-bca2-eeb23741b1af" />
<img width="498" height="724" alt="Image" src="https://github.com/user-attachments/assets/f40703e9-7f68-4aa5-88ba-3a07a29c02b0" />

- ### 动漫漫画

<img width="1470" height="790" alt="Image" src="https://github.com/user-attachments/assets/b7f974aa-84ab-48a1-ab42-4fd7ebb5b0e0" />
<img width="1470" height="790" alt="Image" src="https://github.com/user-attachments/assets/1124277a-eb5b-4f6c-8b07-24ce7b672581" />
<img width="1470" height="796" alt="Image" src="https://github.com/user-attachments/assets/21caccf3-be96-4133-a992-81aabe5dfb32" />
<img width="696" height="771" alt="Image" src="https://github.com/user-attachments/assets/3cb4b72b-9806-40cd-a1d1-b7d24a4d7744" />
<img width="727" height="773" alt="Image" src="https://github.com/user-attachments/assets/a324e6d7-e8e5-48a6-a8c9-9585787d26eb" />
<img width="687" height="780" alt="Image" src="https://github.com/user-attachments/assets/a5cc1133-e29e-4d2f-bbda-af9c533378e8" />

- ### 实时黄金

<img width="1470" height="794" alt="Image" src="https://github.com/user-attachments/assets/d3a8e9c0-adc1-40f1-9de9-f79c14f483a4" />
<img width="1470" height="795" alt="Image" src="https://github.com/user-attachments/assets/b17a1d6d-d8e8-48f6-96f6-a49968d4be71" />
<img width="660" height="700" alt="Image" src="https://github.com/user-attachments/assets/41da09a3-8c84-434c-a001-fc81b5624217" />

- ### ChatGPT页面

<img width="1470" height="794" alt="Image" src="https://github.com/user-attachments/assets/d47c6638-b2e5-4b86-b973-61717c6c207a" />
<img width="1470" height="791" alt="Image" src="https://github.com/user-attachments/assets/7378bfbc-f23a-44d5-b575-1f44c3c477e0" />
<img width="1470" height="795" alt="Image" src="https://github.com/user-attachments/assets/56c03b75-1a42-4002-bc20-48c5661feec8" />
<img width="701" height="770" alt="Image" src="https://github.com/user-attachments/assets/fa617f4c-cd29-4902-ada4-10cdff51637f" />

- ### 音乐页面

<img width="1470" height="789" alt="Image" src="https://github.com/user-attachments/assets/72f44648-1e81-435a-b4ea-98ce4f21f6c2" />
<img width="1470" height="794" alt="Image" src="https://github.com/user-attachments/assets/086ee166-1702-43bc-92b8-3a5baead925f" />
<img width="1470" height="791" alt="Image" src="https://github.com/user-attachments/assets/a243878b-838d-464a-8953-98342ed1c8b4" />
<img width="671" height="791" alt="Image" src="https://github.com/user-attachments/assets/004c7a1a-4f48-4e5d-a918-ad3168ef2170" />

- ### 闲鱼助手

<img width="1470" height="793" alt="Image" src="https://github.com/user-attachments/assets/f2502814-f88b-44ee-94a3-47b0180b6e1e" />
<img width="487" height="716" alt="Image" src="https://github.com/user-attachments/assets/a4d1de7e-6375-4d4d-b10c-749c14b21449" />

- ### 文生图页面

<img width="1470" height="794" alt="Image" src="https://github.com/user-attachments/assets/95ca9824-d33a-4d51-ab72-9c9df4f80df5" />
<img width="1470" height="795" alt="Image" src="https://github.com/user-attachments/assets/cae32b98-c081-4d46-a7e0-2c676727a668" />

- ### 降重页面

<img width="1470" height="716" alt="Image" src="https://github.com/user-attachments/assets/7db6a99a-b7f6-4771-93a5-0a4bf6ebd68d" />
<img width="487" height="724" alt="Image" src="https://github.com/user-attachments/assets/a4cf7de6-5962-4e03-b77b-28944c584989" />

- ### Agent监工

<img width="1470" height="794" alt="Image" src="https://github.com/user-attachments/assets/d17c4478-a912-4db5-8572-1ac021b05a3d" />

- ### 404页面

<img width="1470" height="792" alt="Image" src="https://github.com/user-attachments/assets/efd0425b-1879-42a1-9c1e-eb71baea8bfe" />

## ⚡ 网站性能

<img width="1470" height="793" alt="Image" src="https://github.com/user-attachments/assets/468c753d-5f3f-43ff-b3d0-e86a66064319" />
<img width="1373" height="788" alt="Image" src="https://github.com/user-attachments/assets/b159956b-599a-439a-a05c-33cd25def60a" />
<img width="1470" height="755" alt="Image" src="https://github.com/user-attachments/assets/746890e5-d67b-491f-af1f-957b0f832040" />
<img width="1276" height="713" alt="Image" src="https://github.com/user-attachments/assets/179ffd95-19a5-41aa-a389-89d7385410a9" />
<img width="1276" height="720" alt="Image" src="https://github.com/user-attachments/assets/7ca5f088-78d2-43b0-a5d8-9b39868f4da0" />
<img width="1276" height="648" alt="Image" src="https://github.com/user-attachments/assets/2ff7eb2b-2c7a-4138-b627-ca59f48493d2" />

<details>

<summary>历史版本V3.0</summary>

> 切换commit版本:bbcb9d9146edcb1230adb874d67c2bb38aac1e69

## 🔰 项目概述

✅ 技术栈

- 前端:Vue2,Vuex,JQuery,Three.js,axios,fetch,路由前置全局守卫,MD5加密,验证码,网站访问次数统计,Markdown语法解析,highlight代码高亮显示,处理EventStream流,PC端屏幕适配,组件库使用ElementUI和Layui
- 后端:Java,开发框架SpringBoot,数据库MySQL,中间件Redis,第三方API接入Openai-ChatGPT,接入文生图模型(MidJourney / Stable Diffusion Model),核心技术包含拦截器、过滤器、本地缓存Caffeine LoadingCache、算法(双端队列 + 滑动窗口 + 轮询负载均衡等)、Stream流、全局异常处理器、定时任务、锁机制、Swagger
- 部署:Nginx,服务器开代理模式

✅ 音乐播放

- 无需登录,可快速上手使用
- 支持歌曲/歌手的模糊搜索,播放歌曲和对应MV
- 支持歌曲倍速播放,MV可下载

✅ 视频解析

- 无需会员,可在线解析视频,解析速度快
- 包含海量视频资源,提供多个可用的解析接口
- 支持全屏、倍速播放,画质超清及以上

✅ ChatGPT

- 集成OpenAi API (ChatGPT3.5),无需再代理,可快速使用
- 支持markdown格式,代码高亮,代码复制,公式和图表展示
- 无限轮聊天 + 带上下文逻辑
- 流式输出,会话存储管理
- 语音聊天 + 语音播放

✅ 文生图

- 接入文生图模型(MidJourney / Stable Diffusion Model)
- 支持大量多语言的AI绘图
- 提供20+种生成的图片风格

## ➰ 效果展示

- ### 检测页面

  ![image](https://github.com/P1kaj1uu/VIP-Video-Parsing/assets/94435057/47f44a74-6901-4c0a-bd28-a69ff2423dae)

- ### 登录页面

  ![image](https://github.com/P1kaj1uu/VIP-Video-Parsing/assets/94435057/9a994b55-b7af-4ee2-98b1-7ec3844be75b)

- ### 404页面

  ![image](https://github.com/P1kaj1uu/VIP-Video-Parsing/assets/94435057/8b1bc61b-a5ff-4dcb-80e8-4459680f8da7)

- ### 爱心跳动

  ![image](https://github.com/P1kaj1uu/VIP-Video-Parsing/assets/94435057/02b9367e-0b6e-4cde-8157-36b5731aa518)

- ### 音乐页面

  ![image](https://github.com/P1kaj1uu/VIP-Video-Parsing/assets/94435057/a02a52f6-15e8-4503-922b-2af207419c15)
  ![image](https://github.com/P1kaj1uu/VIP-Video-Parsing/assets/94435057/1bbd4733-f5fa-43ed-bd4d-a2c30a5b8f52)
  ![image](https://github.com/P1kaj1uu/VIP-Video-Parsing/assets/94435057/1321776c-1942-4ab4-b911-5569759b80d3)
  ![image](https://github.com/P1kaj1uu/VIP-Video-Parsing/assets/94435057/0b7d8db6-d8ad-4e6e-a103-427e66deba33)

- ### 观看页面

  ![image](https://github.com/P1kaj1uu/VIP-Video-Parsing/assets/94435057/8d26a5d2-9cb9-4671-8612-a65e1dd1adda)
  ![image](https://github.com/P1kaj1uu/VIP-Video-Parsing/assets/94435057/c02fafa0-2f08-4f09-bb92-bf4480dcf7e2)
  ![image](https://github.com/P1kaj1uu/VIP-Video-Parsing/assets/94435057/2dc87edc-068e-48f4-8655-d7c05ff91086)
  ![image](https://github.com/P1kaj1uu/VIP-Video-Parsing/assets/94435057/0464986a-b635-4114-9b41-84e7786fdabb)
  ![image](https://github.com/P1kaj1uu/VIP-Video-Parsing/assets/94435057/43d7a201-0473-4d29-bc3c-d1fd03898e85)
  ![image](https://github.com/P1kaj1uu/VIP-Video-Parsing/assets/94435057/3f77676f-fd12-497e-ba5b-857429a60dbb)

- ### 降重页面

  ![image](https://github.com/P1kaj1uu/VIP-Video-Parsing/assets/94435057/05dc42f5-15ed-49e7-9141-dead78fd5a5b)

- ### ChatGPT页面

  ![image](https://github.com/P1kaj1uu/VIP-Video-Parsing/assets/94435057/112e3eb6-158f-47e5-9bb4-7e104af38088)
  ![image](https://github.com/P1kaj1uu/VIP-Video-Parsing/assets/94435057/f219dc3a-2540-4c87-8ac3-1d030ca88f7e)
  ![image](https://github.com/P1kaj1uu/VIP-Video-Parsing/assets/94435057/d44844db-fd03-430c-8bc7-ebbbdf6abed2)
  ![image](https://github.com/P1kaj1uu/VIP-Video-Parsing/assets/94435057/fe6d07d7-4564-458b-aa11-d05b1d7119ec)

- ### 文生图页面
  ![image](https://github.com/P1kaj1uu/VIP-Video-Parsing/assets/94435057/26a6ac68-a3c9-49f1-b53d-47582480b107)
  ![image](https://github.com/P1kaj1uu/VIP-Video-Parsing/assets/94435057/fcf5de85-6a43-42a0-88e7-ca1bc2d8e6bb)
  ![image](https://github.com/P1kaj1uu/VIP-Video-Parsing/assets/94435057/05f02f4a-836a-4d68-879d-5b2d7264bf02)
  ![image](https://github.com/P1kaj1uu/VIP-Video-Parsing/assets/94435057/61f8d79d-13b6-4700-98e5-e316023c2e16)

## ⚡ 网站性能

![image](https://github.com/P1kaj1uu/VIP-Video-Parsing/assets/94435057/8bf28d51-e295-4eb7-bf81-d89ba65d2527)
![image](https://github.com/P1kaj1uu/VIP-Video-Parsing/assets/94435057/f0300dc2-945c-4f49-8b62-083dd3d5911b)
![image](https://github.com/P1kaj1uu/VIP-Video-Parsing/assets/94435057/f8ff05cf-c2f8-4967-a5f5-2975b2d729c0)

</details>

## 🖋 参与贡献

<a href="https://github.com/P1Kaj1uu/VIP-Video-Parsing/graphs/contributors"> <img src="https://contrib.rocks/image?repo=P1Kaj1uu/VIP-Video-Parsing" /></a>

## 🍺 赞助

如果你认为我的项目对你很有帮助,而且情况允许的话,那么请考虑支持我的项目。我将非常感激任何的支持,哪怕只是一点点的资助,也能激励我持续开发和改进这个项目。

您可以通过以下几种方式支持我的项目:

- 赞助我:您可以通过贡献资金来支持我的项目,这将帮助我支付服务器、工具和其他开发成本。您可以在下方找到资助方式。

- 分享项目:如果您不能贡献资金,但是您认为我的项目非常有价值,那么请考虑分享项目链接给您的朋友和同事。这将有助于我的项目得到更多的关注和支持。如果可以请给一个小小的star!

- 提供反馈:您可以通过提交Issues或者Pull Requests来帮助改进我的项目。如果您发现了任何错误或者您认为我的项目可以改进的地方,欢迎随时向我提供反馈。

总之,非常感谢您对我的项目的支持,我将努力不懈地改进和提高这个项目的质量,让它更好地为您和其他用户服务。

<br />

联系我(WeChat:Dveiklokk):

<img width="274" height="381" alt="Image" src="https://github.com/user-attachments/assets/16f145fa-af7f-4ef2-9e36-7051c619eaa9" />

WeChat Pay:

<img width="263" height="375" alt="Image" src="https://github.com/user-attachments/assets/b3174698-024c-4be4-bd9b-7cc219503344" />

## ⏰ Star History

[![Star History Chart](https://api.star-history.com/svg?repos=P1kaj1uu/ChattyPlay-Agent&type=Timeline)](https://star-history.com/#P1kaj1uu/ChattyPlay-Agent&Timeline)


================================================
FILE: api/index.ts
================================================
import { Hono } from 'hono'
import { cors } from 'hono/cors'
import CryptoJS from 'crypto-js'

const app = new Hono()

// CORS
app.use('/*', cors())

// ============ 认证相关常量 ============

const JWT_SECRET = 'chattyplay-jwt-secret-2024'
const PASSWORD_SECRET = 'chattyplay-secret-key-2024'
const TOKEN_EXPIRY = 7 * 24 * 60 * 60 * 1000 // 7天

// Redis 配置
const REDIS_REST_URL = process.env.UPSTASH_REDIS_REST_URL
const REDIS_REST_TOKEN = process.env.UPSTASH_REDIS_REST_TOKEN

// 用户计数器 key
const USER_COUNTER_KEY = 'user:id_counter'
// 用户 Hash key prefix
const USER_HASH_PREFIX = 'user:'
// 用户名索引 key
const USERNAME_INDEX_KEY = 'user:username_index'

// ============ Redis HTTP API 调用函数 ============

interface RedisResult {
  result?: any
  error?: string
}

async function redisCommand(command: string[]): Promise<RedisResult> {
  if (!REDIS_REST_URL || !REDIS_REST_TOKEN) {
    return { error: 'Redis 未配置' }
  }

  try {
    const response = await fetch(`${REDIS_REST_URL}`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${REDIS_REST_TOKEN}`
      },
      body: JSON.stringify(command)
    })

    if (!response.ok) {
      return { error: `Redis 请求失败: ${response.status}` }
    }

    const data = await response.json()
    return { result: data.result }
  } catch (error) {
    return { error: `Redis 连接错误: ${error instanceof Error ? error.message : '未知错误'}` }
  }
}

// Redis 操作函数
async function getNextUserId(): Promise<number | null> {
  const { result, error } = await redisCommand(['INCR', USER_COUNTER_KEY])
  if (error || result === null) return null
  return result as number
}

async function getUserById(id: number): Promise<User | null> {
  const { result, error } = await redisCommand(['HGETALL', `${USER_HASH_PREFIX}${id}`])
  if (error || !result || result.length === 0) return null

  // HGETALL 返回 [key1, val1, key2, val2, ...] 格式
  const user: any = {}
  for (let i = 0; i < result.length; i += 2) {
    user[result[i]] = result[i + 1]
  }
  user.id = Number(user.id)
  return user as User
}

async function getUserByUsername(username: string): Promise<User | null> {
  // 从用户名索引获取用户 ID
  const { result: userId, error } = await redisCommand(['HGET', USERNAME_INDEX_KEY, username])
  if (error || !userId) return null
  return getUserById(Number(userId))
}

async function getUserByEmail(email: string): Promise<User | null> {
  // 扫描查找邮箱匹配的用户(较慢但可行)
  const { result: keys, error } = await redisCommand(['KEYS', `${USER_HASH_PREFIX}*`])
  if (error || !keys || keys.length === 0) return null

  for (const key of keys) {
    const { result: emailVal, error: emailError } = await redisCommand(['HGET', key, 'email'])
    if (!emailError && emailVal === email) {
      return getUserById(Number(key.replace(USER_HASH_PREFIX, '')))
    }
  }
  return null
}

async function createUser(user: User): Promise<void> {
  const { error } = await redisCommand([
    'HMSET',
    `${USER_HASH_PREFIX}${user.id}`,
    'id', String(user.id),
    'username', user.username,
    'password', user.password,
    'email', user.email || '',
    'created_at', user.created_at
  ])
  if (error) throw new Error(error)

  // 保存用户名索引
  await redisCommand(['HSET', USERNAME_INDEX_KEY, user.username, String(user.id)])
}

async function updateUserField(id: number, field: string, value: string): Promise<void> {
  await redisCommand(['HSET', `${USER_HASH_PREFIX}${id}`, field, value])
}

// ============ 数据类型定义 ============

interface User {
  id: number
  username: string
  password: string
  email?: string
  avatar?: string
  created_at: string
  last_login?: string
}

interface UserWithoutPassword {
  id: number
  username: string
  email?: string
  avatar?: string
  created_at: string
  last_login?: string
}

// ============ 密码工具函数 ============

function hashPassword(password: string): string {
  return CryptoJS.SHA256(password + PASSWORD_SECRET).toString()
}

function verifyPassword(password: string, hashedPassword: string): boolean {
  const hashed = hashPassword(password)
  return hashed === hashedPassword
}

// ============ JWT Token 函数 ============

function generateToken(payload: { userId: number; username: string }): string {
  const tokenPayload = {
    ...payload,
    exp: Date.now() + TOKEN_EXPIRY
  }

  const header = {
    alg: 'HS256',
    typ: 'JWT'
  }

  const encodedHeader = base64UrlEncode(JSON.stringify(header))
  const encodedPayload = base64UrlEncode(JSON.stringify(tokenPayload))
  const signature = CryptoJS.HmacSHA256(`${encodedHeader}.${encodedPayload}`, JWT_SECRET).toString()
  const encodedSignature = base64UrlEncode(signature)

  return `${encodedHeader}.${encodedPayload}.${encodedSignature}`
}

function verifyToken(token: string): { userId: number; username: string } | null {
  try {
    const [encodedHeader, encodedPayload, encodedSignature] = token.split('.')

    if (!encodedHeader || !encodedPayload || !encodedSignature) {
      return null
    }

    const signature = CryptoJS.HmacSHA256(`${encodedHeader}.${encodedPayload}`, JWT_SECRET).toString()
    const encodedSignatureCheck = base64UrlEncode(signature)

    if (encodedSignature !== encodedSignatureCheck) {
      return null
    }

    const payload = JSON.parse(base64UrlDecode(encodedPayload)) as { userId: number; username: string; exp?: number }

    if (payload.exp && payload.exp < Date.now()) {
      return null
    }

    return payload
  } catch (error) {
    console.error('Token verification failed:', error)
    return null
  }
}

function base64UrlEncode(str: string): string {
  return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(str))
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=/g, '')
}

function base64UrlDecode(str: string): string {
  let base64 = str.replace(/-/g, '+').replace(/_/g, '/')
  while (base64.length % 4) {
    base64 += '='
  }
  return CryptoJS.enc.Base64.parse(base64).toString(CryptoJS.enc.Utf8)
}

// ============ 认证路由 ============

/**
 * 用户注册
 * POST /api/auth/register
 */
app.post('/api/auth/register', async (c) => {
  try {
    const body = await c.req.json()
    const { username, password, email } = body

    if (!username || !password) {
      return c.json({
        success: false,
        message: '用户名和密码不能为空'
      }, 400)
    }

    if (username.length < 3 || username.length > 20) {
      return c.json({
        success: false,
        message: '用户名长度必须在3-20个字符之间'
      }, 400)
    }

    if (password.length < 6 || password.length > 20) {
      return c.json({
        success: false,
        message: '密码长度必须在6-20个字符之间'
      }, 400)
    }

    if (email) {
      const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
      if (!emailRegex.test(email)) {
        return c.json({
          success: false,
          message: '邮箱格式不正确'
        }, 400)
      }
    }

    // 检查用户名是否已存在
    const existingUser = await getUserByUsername(username)
    if (existingUser) {
      return c.json({
        success: false,
        message: '用户名已存在'
      }, 400)
    }

    // 检查邮箱是否已存在
    if (email) {
      const existingEmail = await getUserByEmail(email)
      if (existingEmail) {
        return c.json({
          success: false,
          message: '邮箱已被注册'
        }, 400)
      }
    }

    // 创建用户
    const userId = await getNextUserId()
    if (userId === null) {
      return c.json({
        success: false,
        message: '用户服务暂不可用,请检查 Redis 配置'
      }, 500)
    }

    const hashedPassword = hashPassword(password)
    const createdAt = new Date().toISOString()

    const newUser: User = {
      id: userId,
      username,
      password: hashedPassword,
      email,
      created_at: createdAt
    }

    await createUser(newUser)

    // 生成 token
    const token = generateToken({
      userId: newUser.id,
      username: newUser.username
    })

    const userWithoutPassword: UserWithoutPassword = {
      id: newUser.id,
      username: newUser.username,
      email: newUser.email,
      created_at: newUser.created_at
    }

    return c.json({
      success: true,
      message: '注册成功',
      token,
      user: userWithoutPassword
    })
  } catch (error) {
    console.error('注册接口错误:', error)
    return c.json({
      success: false,
      message: '服务器错误'
    }, 500)
  }
})

/**
 * 用户登录
 * POST /api/auth/login
 */
app.post('/api/auth/login', async (c) => {
  try {
    const body = await c.req.json()
    const { username, password } = body

    if (!username || !password) {
      return c.json({
        success: false,
        message: '用户名和密码不能为空'
      }, 400)
    }

    // 查找用户
    const user = await getUserByUsername(username)
    if (!user) {
      return c.json({
        success: false,
        message: '用户名或密码错误'
      }, 400)
    }

    // 验证密码
    const isPasswordValid = verifyPassword(password, user.password)
    if (!isPasswordValid) {
      return c.json({
        success: false,
        message: '用户名或密码错误'
      }, 400)
    }

    // 更新最后登录时间
    const lastLogin = new Date().toISOString()
    await updateUserField(user.id, 'last_login', lastLogin)

    // 生成 token
    const token = generateToken({
      userId: user.id,
      username: user.username
    })

    const userWithoutPassword: UserWithoutPassword = {
      id: user.id,
      username: user.username,
      email: user.email,
      avatar: user.avatar,
      created_at: user.created_at,
      last_login: lastLogin
    }

    return c.json({
      success: true,
      message: '登录成功',
      token,
      user: userWithoutPassword
    })
  } catch (error) {
    console.error('登录接口错误:', error)
    return c.json({
      success: false,
      message: '服务器错误'
    }, 500)
  }
})

/**
 * 验证 token 并获取用户信息
 * GET /api/auth/me
 */
app.get('/api/auth/me', async (c) => {
  try {
    const token = c.req.header('Authorization')?.replace('Bearer ', '')

    if (!token) {
      return c.json({
        success: false,
        message: '未提供认证令牌'
      }, 401)
    }

    const payload = verifyToken(token)

    if (!payload) {
      return c.json({
        success: false,
        message: '无效的认证令牌'
      }, 401)
    }

    // 查找用户
    const user = await getUserById(payload.userId)
    if (!user) {
      return c.json({
        success: false,
        message: '用户不存在'
      }, 401)
    }

    const userWithoutPassword: UserWithoutPassword = {
      id: user.id,
      username: user.username,
      email: user.email,
      avatar: user.avatar,
      created_at: user.created_at,
      last_login: user.last_login
    }

    return c.json({
      success: true,
      user: userWithoutPassword
    })
  } catch (error) {
    console.error('获取用户信息接口错误:', error)
    return c.json({
      success: false,
      message: '服务器错误'
    }, 500)
  }
})

// 简单的健康检查
app.get('/api/health', (c) => c.json({ status: 'ok' }))

// 视频下载代理端点
app.post('/api/resolve', async (c) => {
  const targetUrl = 'https://xiazaishipin.com/api/resolve'
  const body = await c.req.json()

  const response = await fetch(targetUrl, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Referer': 'https://xiazaishipin.com/',
      'Origin': 'https://xiazaishipin.com',
      'Accept': 'application/json, text/plain, */*',
      'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
    },
    body: JSON.stringify(body),
  })

  const responseData = await response.json()

  if (responseData.thumbnail) {
    responseData.thumbnail = `/api/image-proxy?url=${encodeURIComponent(responseData.thumbnail)}`
  }

  return c.json(responseData, response.status as any)
})

// 图片代理 - 绕过防盗链
app.get('/api/image-proxy', async (c) => {
  const imageUrl = c.req.query('url')

  if (!imageUrl) {
    return c.json({ error: 'Missing url parameter' }, 400)
  }

  try {
    const response = await fetch(imageUrl, {
      headers: {
        'Referer': 'https://www.bilibili.com/',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
      },
    })

    if (!response.ok) {
      throw new Error(`Failed to fetch image: ${response.status}`)
    }

    const imageBuffer = await response.arrayBuffer()
    const contentType = response.headers.get('Content-Type') || 'image/jpeg'

    return new Response(imageBuffer, {
      headers: {
        'Content-Type': contentType,
        'Cache-Control': 'public, max-age=86400',
      },
    })
  } catch (error) {
    return c.json({
      error: 'Failed to proxy image',
      message: error instanceof Error ? error.message : 'Unknown error'
    }, 500)
  }
})

// B站视频下载代理
app.post('/api/bilibili-download', async (c) => {
  const targetUrl = 'https://xiazaishipin.com/api/bilibili-download'
  const body = await c.req.json()

  const response = await fetch(targetUrl, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Referer': 'https://xiazaishipin.com/',
      'Origin': 'https://xiazaishipin.com',
      'Accept': '*/*',
      'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
    },
    body: JSON.stringify(body),
  })

  const contentType = response.headers.get('content-type') || ''

  if (contentType.includes('application/json')) {
    const jsonData = await response.json()
    return c.json(jsonData)
  } else {
    const reader = response.body?.getReader()

    if (!reader) {
      throw new Error('无法获取响应流')
    }

    const stream = new ReadableStream({
      async start(controller) {
        try {
          while (true) {
            const { done, value } = await reader.read()
            if (done) {
              controller.close()
              break
            }
            controller.enqueue(value)
          }
        } catch (error) {
          controller.error(error)
        } finally {
          reader.releaseLock()
        }
      }
    })

    return new Response(stream, {
      status: response.status,
      headers: {
        'Content-Type': contentType,
        'Content-Disposition': response.headers.get('Content-Disposition') || `attachment; filename="video.mp4"`,
        'Content-Length': response.headers.get('Content-Length') || '',
        'Cache-Control': 'no-cache',
      },
    })
  }
})

// 抖音/其他视频下载代理
app.post('/api/proxy-download', async (c) => {
  const targetUrl = 'https://xiazaishipin.com/api/proxy-download'
  const body = await c.req.json()

  const response = await fetch(targetUrl, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Referer': 'https://xiazaishipin.com/',
      'Origin': 'https://xiazaishipin.com',
      'Accept': '*/*',
      'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
    },
    body: JSON.stringify(body),
  })

  const contentType = response.headers.get('content-type') || ''

  if (contentType.includes('application/json')) {
    const jsonData = await response.json()
    return c.json(jsonData)
  } else {
    const reader = response.body?.getReader()

    if (!reader) {
      throw new Error('无法获取响应流')
    }

    const stream = new ReadableStream({
      async start(controller) {
        try {
          while (true) {
            const { done, value } = await reader.read()
            if (done) {
              controller.close()
              break
            }
            controller.enqueue(value)
          }
        } catch (error) {
          controller.error(error)
        } finally {
          reader.releaseLock()
        }
      }
    })

    return new Response(stream, {
      status: response.status,
      headers: {
        'Content-Type': contentType,
        'Content-Disposition': response.headers.get('Content-Disposition') || `attachment; filename="video.mp4"`,
        'Content-Length': response.headers.get('Content-Length') || '',
        'Cache-Control': 'no-cache',
      },
    })
  }
})

// ============ LaTeX 编译 API 代理 ============

// LaTeX 编译 API 代理
app.post('/api/latex/compile', async (c) => {
  try {
    const { code, compiler = 'xelatex', files = [] } = await c.req.json()

    // 添加时间戳确保请求唯一性
    const timestamp = new Date().toISOString()
    console.log(`[${timestamp}] LaTeX 编译请求`)

    if (!code || typeof code !== 'string') {
      return c.json({
        success: false,
        error: '请提供有效的 LaTeX 代码'
      }, 400)
    }

    console.log('LaTeX 编译请求:', {
      compiler,
      codeLength: code.length,
      codePreview: code.substring(0, 100) + (code.length > 100 ? '...' : ''),
      filesCount: files.length,
      files: files.map((f: any) => ({ name: f.name, contentLength: f.content?.length }))
    })

    // 如果有图片文件,使用正确的 API 格式传递
    const processedCode = code
    if (files && files.length > 0) {
      console.log('检测到图片文件,使用多文件资源格式')
    }

    // 检测是否包含中文字符
    const hasChinese = /[一-龥]/.test(processedCode)
    console.log('包含中文:', hasChinese)

    // 如果包含中文,尝试使用专门处理中文的服务
    if (hasChinese) {
      console.log('检测到中文内容,使用特殊的中文处理方案')

      // 方案1: 尝试使用 Overleaf-style API
      try {
        const overleafUrl = 'https://compile.overleaf.com/docs'

        // 构建文件列表:主文件(图片已嵌入)
        const requestFiles: any[] = [{
          name: 'main.tex',
          content: processedCode
        }]

        const response = await fetch(overleafUrl, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            compiler: compiler,
            files: requestFiles,
            options: {
              timeout: 60
            }
          }),
        })

        if (response.ok) {
          const data = await response.json()
          if (data.pdfs && data.pdfs['main.pdf']) {
            const pdfData = data.pdfs['main.pdf']
            const pdfBuffer = Buffer.from(pdfData, 'base64')

            return new Response(pdfBuffer, {
              headers: {
                'Content-Type': 'application/pdf',
                'Content-Disposition': 'attachment; filename="document.pdf"',
                'Access-Control-Allow-Origin': '*',
              },
            })
          }
        }
      } catch (error) {
        console.error('Overleaf API 失败:', error)
      }

      // 方案2: 修改LaTeX代码以使用更通用的字体设置
      console.log('尝试修改LaTeX代码以支持中文')
      let modifiedCode = processedCode

      // 如果代码中没有ctex相关包,添加中文支持
      if (!processedCode.includes('ctex') && !processedCode.includes('CJK')) {
        // 在documentclass后添加中文支持
        modifiedCode = processedCode.replace(
          /\\documentclass(\[[^\]]*\])?\{([^}]+)\}/,
          (match: string, options: string, docclass: string) => {
            if (docclass === 'ctexart') {
              return match
            }
            return `\\documentclass${options || ''}{${docclass}}\n\\usepackage{CJKutf8}\n\\usepackage{CJK}`
          }
        )

        // 在document环境中添加CJK支持
        if (modifiedCode.includes('\\begin{document}')) {
          modifiedCode = modifiedCode.replace(
            '\\begin{document}',
            '\\begin{document}\n\\begin{CJK*}{UTF8}{gbsn}'
          )
        }

        if (modifiedCode.includes('\\end{document}')) {
          modifiedCode = modifiedCode.replace(
            '\\end{document}',
            '\\end{CJK*}\n\\end{document}'
          )
        }
      }

      console.log('修改后的代码预览:', modifiedCode.substring(0, 200) + '...')

      // 使用修改后的代码尝试编译
      const latexApiUrl = 'https://latex.ytotech.com/builds/sync'

      // 构建请求数据 - 使用正确的 resources 格式
      const resources: any[] = [{
        main: true,
        content: modifiedCode
      }]

      // 添加附加文件(图片等)- 使用正确的格式
      if (files && files.length > 0) {
        for (const file of files) {
          if (file.name && file.content) {
            let base64Data = file.content
            if (base64Data.startsWith('data:')) {
              base64Data = base64Data.split(',')[1] || base64Data
            }

            resources.push({
              path: file.name,
              file: base64Data
            })

            console.log(`添加图片资源(中文处理): ${file.name}`)
          }
        }
      }

      const requestData = {
        compiler: compiler,
        resources: resources
      }

      console.log('发送到 LaTeX API 的请求(中文处理):', {
        compiler,
        codeLength: modifiedCode.length,
        资源数量: resources.length,
        包含图片: files && files.length > 0
      })

      const response = await fetch(latexApiUrl, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(requestData),
      })

      if (response.ok) {
        const contentType = response.headers.get('content-type') || ''
        if (contentType.includes('application/pdf')) {
          const pdfBuffer = await response.arrayBuffer()
          console.log('中文PDF生成成功,大小:', pdfBuffer.byteLength, '字节')

          return new Response(pdfBuffer, {
            headers: {
              'Content-Type': 'application/pdf',
              'Content-Disposition': 'attachment; filename="document.pdf"',
              'Access-Control-Allow-Origin': '*',
            },
          })
        }
      }
    }

    // 非中文内容或中文处理失败,使用标准流程
    const latexApiUrl = 'https://latex.ytotech.com/builds/sync'

    // 构建请求数据 - 使用正确的 resources 格式
    const resources: any[] = [{
      main: true,
      content: processedCode
    }]

    // 添加附加文件(图片等)- 使用正确的格式
    if (files && files.length > 0) {
      for (const file of files) {
        if (file.name && file.content) {
          let base64Data = file.content
          if (base64Data.startsWith('data:')) {
            base64Data = base64Data.split(',')[1] || base64Data
          }

          resources.push({
            path: file.name,
            file: base64Data
          })

          console.log(`添加图片资源: ${file.name}`)
        }
      }
    }

    const requestData = {
      compiler: compiler,
      resources: resources
    }

    console.log('发送到 LaTeX API 的请求:', {
      compiler,
      codeLength: processedCode.length,
      资源数量: resources.length,
      包含图片: files && files.length > 0
    })

    const response = await fetch(latexApiUrl, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(requestData),
    })

    console.log('API 响应状态:', response.status, response.statusText)

    if (!response.ok) {
      const errorText = await response.text()
      console.error('LaTeX API 错误响应:', errorText)
      console.error('请求体详情:', JSON.stringify(requestData, null, 2))
      throw new Error(`LaTeX API 返回错误: ${response.status} ${response.statusText} - ${errorText}`)
    }

    const contentType = response.headers.get('content-type') || ''
    console.log('响应内容类型:', contentType)

    if (contentType.includes('application/pdf')) {
      const pdfBuffer = await response.arrayBuffer()
      console.log('PDF 生成成功,大小:', pdfBuffer.byteLength, '字节')

      return new Response(pdfBuffer, {
        headers: {
          'Content-Type': 'application/pdf',
          'Content-Disposition': 'attachment; filename="document.pdf"',
          'Access-Control-Allow-Origin': '*',
          'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
          'Access-Control-Allow-Headers': 'Content-Type, Authorization',
        },
      })
    }

    const errorText = await response.text()
    console.error('非 PDF 响应:', errorText)
    return c.json({
      success: false,
      error: `编译失败,服务器返回非 PDF 内容: ${errorText.substring(0, 200)}`
    }, 500)

  } catch (error) {
    console.error('LaTeX 编译代理错误:', error)

    const errorMessage = error instanceof Error ? error.message : '未知错误'
    return c.json({
      success: false,
      error: `LaTeX 编译失败: ${errorMessage}`
    }, 500)
  }
})

// LaTeX 编译健康检查
app.get('/api/latex/health', async (c) => {
  try {
    const testCode = '\\documentclass{article}\\begin{document}Hello World\\end{document}'
    const response = await fetch(`https://latex.ytotech.com/builds/sync?content=${encodeURIComponent(testCode)}`, {
      method: 'GET',
    })

    return c.json({
      status: response.ok ? 'available' : 'unavailable',
      statusCode: response.status,
      timestamp: new Date().toISOString()
    })
  } catch (error) {
    return c.json({
      status: 'error',
      error: error instanceof Error ? error.message : '未知错误',
      timestamp: new Date().toISOString()
    }, 503)
  }
})

export default app


================================================
FILE: deploy.sh
================================================
#!/bin/bash

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

# 项目名称
PROJECT_NAME="chattyplay-agent"
IMAGE_NAME="${PROJECT_NAME}:latest"
CONTAINER_NAME="${PROJECT_NAME}-web"

echo -e "${GREEN}======================================${NC}"
echo -e "${GREEN}  Docker 部署脚本${NC}"
echo -e "${GREEN}======================================${NC}"

# 检查 Docker 是否安装
if ! command -v docker &> /dev/null; then
    echo -e "${RED}错误: Docker 未安装,请先安装 Docker${NC}"
    exit 1
fi

# 构建镜像
echo -e "${YELLOW}步骤 1/4: 构建 Docker 镜像...${NC}"
docker build -t $IMAGE_NAME .
if [ $? -ne 0 ]; then
    echo -e "${RED}镜像构建失败${NC}"
    exit 1
fi
echo -e "${GREEN}✓ 镜像构建成功${NC}"

# 停止并删除旧容器
echo -e "${YELLOW}步骤 2/4: 停止旧容器...${NC}"
if [ "$(docker ps -aq -f name=$CONTAINER_NAME)" ]; then
    docker stop $CONTAINER_NAME 2>/dev/null
    docker rm $CONTAINER_NAME 2>/dev/null
    echo -e "${GREEN}✓ 旧容器已删除${NC}"
else
    echo -e "${GREEN}✓ 没有运行中的旧容器${NC}"
fi

# 运行新容器
echo -e "${YELLOW}步骤 3/4: 启动新容器...${NC}"
docker run -d \
    --name $CONTAINER_NAME \
    -p 80:80 \
    --restart unless-stopped \
    $IMAGE_NAME

if [ $? -ne 0 ]; then
    echo -e "${RED}容器启动失败${NC}"
    exit 1
fi
echo -e "${GREEN}✓ 容器启动成功${NC}"

# 查看容器状态
echo -e "${YELLOW}步骤 4/4: 检查容器状态...${NC}"
docker ps -f name=$CONTAINER_NAME

echo -e "${GREEN}======================================${NC}"
echo -e "${GREEN}✓ 部署完成!${NC}"
echo -e "${GREEN}访问地址: http://localhost${NC}"
echo -e "${GREEN}======================================${NC}"

# 显示日志
echo -e "${YELLOW}容器日志 (按 Ctrl+C 退出):${NC}"
docker logs -f $CONTAINER_NAME


================================================
FILE: docker-compose.yml
================================================
version: '3.8'

services:
  # 前端应用
  web:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: chattyplay-web
    ports:
      - "80:80"
    environment:
      - NODE_ENV=production
    restart: unless-stopped
    networks:
      - app-network
    healthcheck:
      test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

networks:
  app-network:
    driver: bridge


================================================
FILE: email.config.d.ts
================================================
export interface EmailConfig {
  PUBLIC_KEY: string
  SERVICE_ID: string
  TEMPLATE_ID: string
  fromName: string
  fromEmail: string
}

export const emailConfig: EmailConfig


================================================
FILE: email.config.js
================================================
/**
 * EmailJS 配置文件
 *
 * 使用说明:
 * 1. 访问 https://www.emailjs.com/ 注册账号
 * 2. 添加邮件服务(Email Service),支持:
 *    - Gmail
 *    - Outlook
 *    - Yahoo Mail
 *    - 或任何支持 SMTP 的邮件服务
 * 3. 创建邮件模板(Email Template)
 * 4. 获取以下配置信息并填入:
 *    - SERVICE_ID: 服务 ID
 *    - TEMPLATE_ID: 模板 ID
 *    - PUBLIC_KEY: 公钥
 *
 * 邮件模板示例:
 * 主题:ChattyPlay - 实时金价
 * 内容:
 * {{email}} 您好,
 * {{to_email}} 您好,
 *
 * 黄金价格实时通知:
 *
 * 国内金价:{{domestic_price}}
 * 国际金价:{{international_price}}
 * 当前时间:{{current_time}}
 *
 * --
 * ChattyPlay - 黄金价格实时监控
 */

export const emailConfig = {
  // EmailJS 公钥(必填)
  PUBLIC_KEY: import.meta.env.VITE_EMAILJS_PUBLIC_KEY || 'YOUR_PUBLIC_KEY_HERE',

  // EmailJS 服务 ID(必填)
  SERVICE_ID: import.meta.env.VITE_EMAILJS_SERVICE_ID || 'YOUR_SERVICE_ID_HERE',

  // EmailJS 模板 ID(必填)
  TEMPLATE_ID: import.meta.env.VITE_EMAILJS_TEMPLATE_ID || 'YOUR_TEMPLATE_ID_HERE',

  // 发件人信息(可选)
  fromName: import.meta.env.VITE_EMAILJS_FROM_NAME || 'ChattyPlay',
  fromEmail: import.meta.env.VITE_EMAILJS_FROM_EMAIL || '891523233@qq.com',
}

export default emailConfig


================================================
FILE: index.html
================================================
<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <link rel="icon" type="image/svg+xml" href="/public/icon.ico" />
  <link rel="preconnect" href="https://challenges.cloudflare.com" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>ChattyPlay</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
  </style>

  <!-- 用户行为分析和页面性能监控 -->
  <script async src="https://www.googletagmanager.com/gtag/js?id=G-G81HHJ6B4C"></script>
  <script>
    window.dataLayer = window.dataLayer || [];
    function gtag() {
      dataLayer.push(arguments);
    }
    gtag('js', new Date());
    gtag('config', 'G-G81HHJ6B4C');
  </script>

  <script src="https://unpkg.com/d3@7.8.5/dist/d3.min.js"></script>
  <script src="https://unpkg.com/markmap-lib@0.15.3/dist/browser/index.js"></script>
  <script src="https://unpkg.com/markmap-view@0.15.3/dist/browser/index.js"></script>
</head>

<body>
  <div id="root"></div>

  <script>
    (function () {
      "use strict";

      // 禁用常用快捷键
      document.addEventListener("keydown", function (e) {
        // F12
        if (e.keyCode === 123) {
          e.preventDefault();
          return false;
        }

        // Ctrl+U (查看源代码)
        if (
          e.ctrlKey &&
          e.shiftKey &&
          (e.keyCode === 73 || e.keyCode === 74 || e.keyCode === 67)
        ) {
          e.preventDefault();
          return false;
        }

        // Ctrl+Shift+C (元素检查 - Mac)
        if (e.metaKey && e.shiftKey && e.keyCode === 67) {
          e.preventDefault();
          return false;
        }

        // Ctrl+U (查看源代码)
        if (e.ctrlKey && e.keyCode === 85) {
          e.preventDefault();
          return false;
        }

        // Mac Cmd+Option+I (开发者工具)
        if (e.metaKey && e.altKey && e.keyCode === 73) {
          e.preventDefault();
          return false;
        }
      });

      // 检测开发者工具 (通过窗口大小变化)
      const devtools = {
        open: false,
        orientation: null,
      };

      const threshold = 160;

      setInterval(function () {
        const widthThreshold =
          window.outerWidth - window.innerWidth > threshold;
        const heightThreshold =
          window.outerHeight - window.innerHeight > threshold;
        const orientation = widthThreshold ? "vertical" : "horizontal";

        if (
          !(heightThreshold && widthThreshold) &&
          ((window.Firebug &&
            window.Firebug.chrome &&
            window.Firebug.chrome.isInitialized) ||
            widthThreshold ||
            heightThreshold)
        ) {
          if (!devtools.open || devtools.orientation !== orientation) {
            devtools.open = true;
            devtools.orientation = orientation;
          }
        } else {
          devtools.open = false;
          devtools.orientation = null;
        }
      }, 500);

      setInterval(function () {
        const startTime = new Date().getTime();
        debugger;
        const endTime = new Date().getTime();
        if (endTime - startTime > 100) {
          // 刷新页面
          window.location.reload();
        }
      }, 2000);

      // 禁用拖拽
      document.addEventListener("dragstart", function (e) {
        e.preventDefault();
        return false;
      });

      console.log(
        "%cChattPlay",
        `padding: 10px 40px;
        font-size: 24px;
        font-weight: 600;
        border-radius: 10px;
        background-color:silver;
        background-image:
          radial-gradient(circle at 100% 150%, silver 24%, white 24%, white 28%, silver 28%, silver 36%, white 36%, white 40%, transparent 40%, transparent),
          radial-gradient(circle at 0 150%, silver 24%, white 24%, white 28%, silver 28%, silver 36%, white 36%, white 40%, transparent 40%, transparent),
          radial-gradient(circle at 50% 100%, white 10%, silver 10%, silver 23%, white 23%, white 30%, silver 30%, silver 43%, white 43%, white 50%, silver 50%, silver 63%, white 63%, white 71%, transparent 71%, transparent),
          radial-gradient(circle at 100% 50%, white 5%, silver 5%, silver 15%, white 15%, white 20%, silver 20%, silver 29%, white 29%, white 34%, silver 34%, silver 44%, white 44%, white 49%, transparent 49%, transparent),
          radial-gradient(circle at 0 50%, white 5%, silver 5%, silver 15%, white 15%, white 20%, silver 20%, silver 29%, white 29%, white 34%, silver 34%, silver 44%, white 44%, white 49%, transparent 49%, transparent);
          background-size: 100px 50px;`,
      );
      console.log(
        "%c项目地址:https://github.com/P1kaj1uu/ChattyPlay-Agent",
        "font-size: 16px; color: blue;",
      );
      console.log(
        "%c欢迎大家提交 issue 和 PR,一起完善共建 ChattyPlay!",
        "font-size: 16px; color: green;",
      );
      console.log(
        "%c[Info] 作者:P1kaj1uu(微信号:Dveiklokk)",
        "font-size: 16px; color: purple;",
      );
    })();
  </script>

  <!-- Fundebug 错误监控 -->
  <script async src="https://js.fundebug.cn/fundebug.2.8.8.min.js"></script>

  <!-- Cloudflare Turnstile 反机器人验证 -->
  <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
  <!-- Fundebug 录屏插件 -->
  <script async src="https://js.fundebug.cn/fundebug.revideo.0.8.0.min.js"></script>
  <script>
    // 配置 Fundebug
    fundebug.apikey =
      "fa-0aa5a027de384efbb4dc8a456c863526";
    fundebug.appversion = "1.0.0";

    // 全局错误监听
    // 监听未捕获的 Promise 错误
    window.addEventListener("unhandledrejection", function (event) {
      fundebug.notifyError(event.reason, {
        metaData: {
          type: "unhandledrejection",
        },
      });
    });

    // 监听全局错误
    window.addEventListener("error", function (event) {
      fundebug.notifyError(event.error || new Error(event.message), {
        metaData: {
          type: "global error",
          filename: event.filename,
          lineno: event.lineno,
          colno: event.colno,
        },
      });
    });
  </script>

  <script type="module" src="/src/main.tsx"></script>
</body>

</html>

================================================
FILE: jsconfig.json
================================================
{
  "compilerOptions": {
    "target": "es5",
    "module": "esnext",
    "baseUrl": "./",
    "moduleResolution": "node",
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  }
}


================================================
FILE: package.json
================================================
{
  "name": "chattyplay-agent",
  "private": true,
  "author": {
    "name": "P1Kaj1uu",
    "email": "891523233@qq.com",
    "url": "https://github.com/P1Kaj1uu"
  },
  "version": "4.9.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "dev:server": "tsx watch src/services/server.ts",
    "build": "tsc && vite build",
    "start": "tsx src/services/server.ts",
    "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview",
    "worker:dev": "wrangler dev src/services/worker.ts",
    "worker:deploy": "wrangler deploy",
    "worker:tail": "wrangler tail"
  },
  "dependencies": {
    "@emailjs/browser": "^4.4.1",
    "@hcaptcha/react-hcaptcha": "^2.0.2",
    "@hono/node-server": "^1.19.9",
    "@hono/node-ws": "^1.3.0",
    "@monaco-editor/react": "^4.7.0",
    "@react-oauth/google": "^0.13.4",
    "@types/better-sqlite3": "^7.6.13",
    "@types/three": "^0.182.0",
    "@types/ws": "^8.18.1",
    "@upstash/redis": "^1.37.0",
    "@vercel/hono": "^0.2.51",
    "antd": "^5.12.0",
    "axios": "^1.6.0",
    "better-sqlite3": "^12.6.2",
    "crypto-js": "^4.2.0",
    "echarts": "^6.0.0",
    "echarts-for-react": "^3.0.6",
    "fundebug-javascript": "^2.8.8",
    "fundebug-revideo": "^0.8.0",
    "gsap": "^3.14.2",
    "highlight.js": "^11.9.0",
    "hono": "^4.11.7",
    "http-proxy-middleware": "^3.0.5",
    "i18next": "^25.8.0",
    "i18next-browser-languagedetector": "^8.2.0",
    "marked": "^11.1.0",
    "monaco-editor": "^0.55.1",
    "nprogress": "^0.2.0",
    "oh-my-live2d": "^0.19.3",
    "openai": "^6.25.0",
    "pdfjs-dist": "^5.6.205",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-i18next": "^16.5.4",
    "react-icons": "^5.5.0",
    "react-pdf": "^10.4.1",
    "react-router-dom": "^6.22.0",
    "simplex-noise": "^4.0.3",
    "styled-components": "^6.1.0",
    "three": "^0.182.0",
    "three-stdlib": "^2.36.1",
    "vite-plugin-oh-my-live2d": "^0.19.3",
    "ws": "^8.19.0"
  },
  "devDependencies": {
    "@types/crypto-js": "^4.2.2",
    "@types/node": "^25.2.1",
    "@types/nprogress": "^0.2.3",
    "@types/react": "^18.2.43",
    "@types/react-dom": "^18.2.17",
    "@types/styled-components": "^5.1.34",
    "@typescript-eslint/eslint-plugin": "^6.14.0",
    "@typescript-eslint/parser": "^6.14.0",
    "@vitejs/plugin-react": "^4.2.1",
    "autoprefixer": "^10.4.24",
    "eslint": "^8.55.0",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-react-refresh": "^0.4.5",
    "postcss": "^8.5.6",
    "sass-embedded": "^1.97.3",
    "tailwindcss": "^3.4.19",
    "tsx": "^4.21.0",
    "typescript": "^5.2.2",
    "vite": "^5.0.8",
    "wrangler": "^3.78.0"
  }
}


================================================
FILE: postcss.config.js
================================================
export default {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}


================================================
FILE: public/.htaccess
================================================
# Apache 配置文件 - 用于生产环境部署
# 支持单页应用(SPA)路由和 API 代理

# 启用代理模块
<IfModule mod_proxy.c>
  ProxyRequests Off
  ProxyPreserveHost On

  # API 代理到宝塔服务器 Hono 服务
  ProxyPass /api http://123.60.91.107:3001/api
  ProxyPassReverse /api http://123.60.91.107:3001/api
</IfModule>

# URL 重写规则
<IfModule mod_rewrite.c>
  RewriteEngine On

  # API 请求代理到宝塔服务器(使用 RewriteRule 作为备用方案)
  RewriteRule ^api/(.*)$ http://123.60.91.107:3001/api/$1 [P,L]

  # SPA 路由支持 - 所有非文件、非API请求返回 index.html
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteCond %{REQUEST_URI} !^/api/
  RewriteRule ^(.*)$ /index.html [L,QSA]
</IfModule>

# 缓存控制
<IfModule mod_headers.c>
  # 静态资源缓存
  <FilesMatch "\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$">
    Header set Cache-Control "max-age=31536000, public"
  </FilesMatch>

  # HTML 文件不缓存
  <FilesMatch "\.html$">
    Header set Cache-Control "no-cache, no-store, must-revalidate"
  </FilesMatch>
</IfModule>

# Gzip 压缩
<IfModule mod_deflate.c>
  AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript application/json
</IfModule>


================================================
FILE: public/index.html
================================================
<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <!-- <title><%= htmlWebpackPlugin.options.title %></title> -->
    <link rel="stylesheet" href="./layui/css/layui.css">
    <link rel="preconnect" href="https://challenges.cloudflare.com" />
    <script src="./layui/layui.js"></script>
    <script src="./three/three.min.js"></script>
    <script src="./three/MeshSurfaceSampler.js"></script>
    <script src="./three/TrackballControls.js"></script>
    <script src="./three/simplex-noise.js"></script>
    <script src="./three/OBJLoader.js"></script>
    <script src="./three/gsap.min.js"></script>
    <script async src="https://unpkg.com/oh-my-live2d@latest"></script>
    <script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
    <!-- Cloudflare Turnstile 反机器人验证 -->
    <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
    <!-- Fundebug 错误监控 -->
    <script async src="https://js.fundebug.cn/fundebug.2.8.8.min.js"></script>
    <!-- Fundebug 录屏插件 -->
    <script async src="https://js.fundebug.cn/fundebug.revideo.0.8.0.min.js"></script>
    <title>不见水星记</title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>


================================================
FILE: public/layui/css/layui.css
================================================
/** layui-v2.3.0 MIT License By https://www.layui.com */
 .layui-inline,img{display:inline-block;vertical-align:middle}.layui-rate,li{list-style:none}h1,h2,h3,h4,h5,h6{font-weight:400}.layui-edge,.layui-header,.layui-inline,.layui-main{position:relative}.layui-btn,.layui-edge,.layui-inline,img{vertical-align:middle}.layui-btn,.layui-disabled,.layui-icon,.layui-unselect{-webkit-user-select:none;-ms-user-select:none;-moz-user-select:none}blockquote,body,button,dd,div,dl,dt,form,h1,h2,h3,h4,h5,h6,input,li,ol,p,pre,td,textarea,th,ul{margin:0;padding:0;-webkit-tap-highlight-color:rgba(0,0,0,0)}a:active,a:hover{outline:0}img{border:none}table{border-collapse:collapse;border-spacing:0}h4,h5,h6{font-size:100%}button,input,optgroup,option,select,textarea{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;outline:0}pre{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word}body{line-height:24px;font:14px Helvetica Neue,Helvetica,PingFang SC,\5FAE\8F6F\96C5\9ED1,Tahoma,Arial,sans-serif}hr{height:1px;margin:10px 0;border:0;clear:both}a{color:#333;text-decoration:none}a:hover{color:#777}a cite{font-style:normal;*cursor:pointer}.layui-border-box,.layui-border-box *{box-sizing:border-box}.layui-box,.layui-box *{box-sizing:content-box}.layui-clear{clear:both;*zoom:1}.layui-clear:after{content:'\20';clear:both;*zoom:1;display:block;height:0}.layui-inline{*display:inline;*zoom:1}.layui-edge{display:inline-block;width:0;height:0;border-width:6px;border-style:dashed;border-color:transparent;overflow:hidden}.layui-edge-top{top:-4px;border-bottom-color:#999;border-bottom-style:solid}.layui-edge-right{border-left-color:#999;border-left-style:solid}.layui-edge-bottom{top:2px;border-top-color:#999;border-top-style:solid}.layui-edge-left{border-right-color:#999;border-right-style:solid}.layui-elip{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-disabled,.layui-disabled:hover{color:#d2d2d2!important;cursor:not-allowed!important}.layui-circle{border-radius:100%}.layui-show{display:block!important}.layui-hide{display:none!important}@font-face{font-family:layui-icon;src:url(../font/iconfont.eot?v=230);src:url(../font/iconfont.eot?v=230#iefix) format('embedded-opentype'),url(../font/iconfont.svg?v=230#iconfont) format('svg'),url(../font/iconfont.woff?v=230) format('woff'),url(../font/iconfont.ttf?v=230) format('truetype')}.layui-icon{font-family:layui-icon!important;font-size:16px;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.layui-icon-reply-fill:before{content:"\e611"}.layui-icon-set-fill:before{content:"\e614"}.layui-icon-menu-fill:before{content:"\e60f"}.layui-icon-search:before{content:"\e615"}.layui-icon-share:before{content:"\e641"}.layui-icon-set-sm:before{content:"\e620"}.layui-icon-engine:before{content:"\e628"}.layui-icon-close:before{content:"\1006"}.layui-icon-close-fill:before{content:"\1007"}.layui-icon-chart-screen:before{content:"\e629"}.layui-icon-star:before{content:"\e600"}.layui-icon-circle-dot:before{content:"\e617"}.layui-icon-chat:before{content:"\e606"}.layui-icon-release:before{content:"\e609"}.layui-icon-list:before{content:"\e60a"}.layui-icon-chart:before{content:"\e62c"}.layui-icon-ok-circle:before{content:"\1005"}.layui-icon-layim-theme:before{content:"\e61b"}.layui-icon-table:before{content:"\e62d"}.layui-icon-right:before{content:"\e602"}.layui-icon-left:before{content:"\e603"}.layui-icon-cart-simple:before{content:"\e698"}.layui-icon-face-cry:before{content:"\e69c"}.layui-icon-face-smile:before{content:"\e6af"}.layui-icon-survey:before{content:"\e6b2"}.layui-icon-tree:before{content:"\e62e"}.layui-icon-upload-circle:before{content:"\e62f"}.layui-icon-add-circle:before{content:"\e61f"}.layui-icon-download-circle:before{content:"\e601"}.layui-icon-templeate-1:before{content:"\e630"}.layui-icon-util:before{content:"\e631"}.layui-icon-face-surprised:before{content:"\e664"}.layui-icon-edit:before{content:"\e642"}.layui-icon-speaker:before{content:"\e645"}.layui-icon-down:before{content:"\e61a"}.layui-icon-file:before{content:"\e621"}.layui-icon-layouts:before{content:"\e632"}.layui-icon-rate-half:before{content:"\e6c9"}.layui-icon-add-circle-fine:before{content:"\e608"}.layui-icon-prev-circle:before{content:"\e633"}.layui-icon-read:before{content:"\e705"}.layui-icon-404:before{content:"\e61c"}.layui-icon-carousel:before{content:"\e634"}.layui-icon-help:before{content:"\e607"}.layui-icon-code-circle:before{content:"\e635"}.layui-icon-water:before{content:"\e636"}.layui-icon-username:before{content:"\e66f"}.layui-icon-find-fill:before{content:"\e670"}.layui-icon-about:before{content:"\e60b"}.layui-icon-location:before{content:"\e715"}.layui-icon-up:before{content:"\e619"}.layui-icon-pause:before{content:"\e651"}.layui-icon-date:before{content:"\e637"}.layui-icon-layim-uploadfile:before{content:"\e61d"}.layui-icon-delete:before{content:"\e640"}.layui-icon-play:before{content:"\e652"}.layui-icon-top:before{content:"\e604"}.layui-icon-friends:before{content:"\e612"}.layui-icon-refresh-3:before{content:"\e9aa"}.layui-icon-ok:before{content:"\e605"}.layui-icon-layer:before{content:"\e638"}.layui-icon-face-smile-fine:before{content:"\e60c"}.layui-icon-dollar:before{content:"\e659"}.layui-icon-group:before{content:"\e613"}.layui-icon-layim-download:before{content:"\e61e"}.layui-icon-picture-fine:before{content:"\e60d"}.layui-icon-link:before{content:"\e64c"}.layui-icon-diamond:before{content:"\e735"}.layui-icon-log:before{content:"\e60e"}.layui-icon-rate-solid:before{content:"\e67a"}.layui-icon-fonts-del:before{content:"\e64f"}.layui-icon-unlink:before{content:"\e64d"}.layui-icon-fonts-clear:before{content:"\e639"}.layui-icon-triangle-r:before{content:"\e623"}.layui-icon-circle:before{content:"\e63f"}.layui-icon-radio:before{content:"\e643"}.layui-icon-align-center:before{content:"\e647"}.layui-icon-align-right:before{content:"\e648"}.layui-icon-align-left:before{content:"\e649"}.layui-icon-loading-1:before{content:"\e63e"}.layui-icon-return:before{content:"\e65c"}.layui-icon-fonts-strong:before{content:"\e62b"}.layui-icon-upload:before{content:"\e67c"}.layui-icon-dialogue:before{content:"\e63a"}.layui-icon-video:before{content:"\e6ed"}.layui-icon-headset:before{content:"\e6fc"}.layui-icon-cellphone-fine:before{content:"\e63b"}.layui-icon-add-1:before{content:"\e654"}.layui-icon-face-smile-b:before{content:"\e650"}.layui-icon-fonts-html:before{content:"\e64b"}.layui-icon-form:before{content:"\e63c"}.layui-icon-cart:before{content:"\e657"}.layui-icon-camera-fill:before{content:"\e65d"}.layui-icon-tabs:before{content:"\e62a"}.layui-icon-fonts-code:before{content:"\e64e"}.layui-icon-fire:before{content:"\e756"}.layui-icon-set:before{content:"\e716"}.layui-icon-fonts-u:before{content:"\e646"}.layui-icon-triangle-d:before{content:"\e625"}.layui-icon-tips:before{content:"\e702"}.layui-icon-picture:before{content:"\e64a"}.layui-icon-more-vertical:before{content:"\e671"}.layui-icon-flag:before{content:"\e66c"}.layui-icon-loading:before{content:"\e63d"}.layui-icon-fonts-i:before{content:"\e644"}.layui-icon-refresh-1:before{content:"\e666"}.layui-icon-rmb:before{content:"\e65e"}.layui-icon-home:before{content:"\e68e"}.layui-icon-user:before{content:"\e770"}.layui-icon-notice:before{content:"\e667"}.layui-icon-login-weibo:before{content:"\e675"}.layui-icon-voice:before{content:"\e688"}.layui-icon-upload-drag:before{content:"\e681"}.layui-icon-login-qq:before{content:"\e676"}.layui-icon-snowflake:before{content:"\e6b1"}.layui-icon-file-b:before{content:"\e655"}.layui-icon-template:before{content:"\e663"}.layui-icon-auz:before{content:"\e672"}.layui-icon-console:before{content:"\e665"}.layui-icon-app:before{content:"\e653"}.layui-icon-prev:before{content:"\e65a"}.layui-icon-website:before{content:"\e7ae"}.layui-icon-next:before{content:"\e65b"}.layui-icon-component:before{content:"\e857"}.layui-icon-more:before{content:"\e65f"}.layui-icon-login-wechat:before{content:"\e677"}.layui-icon-shrink-right:before{content:"\e668"}.layui-icon-spread-left:before{content:"\e66b"}.layui-icon-camera:before{content:"\e660"}.layui-icon-note:before{content:"\e66e"}.layui-icon-refresh:before{content:"\e669"}.layui-icon-female:before{content:"\e661"}.layui-icon-male:before{content:"\e662"}.layui-icon-password:before{content:"\e673"}.layui-icon-senior:before{content:"\e674"}.layui-icon-theme:before{content:"\e66a"}.layui-icon-tread:before{content:"\e6c5"}.layui-icon-praise:before{content:"\e6c6"}.layui-icon-star-fill:before{content:"\e658"}.layui-icon-rate:before{content:"\e67b"}.layui-icon-template-1:before{content:"\e656"}.layui-icon-vercode:before{content:"\e679"}.layui-icon-cellphone:before{content:"\e678"}.layui-icon-screen-full:before{content:"\e622"}.layui-icon-screen-restore:before{content:"\e758"}.layui-main{width:1140px;margin:0 auto}.layui-header{z-index:1000;height:60px}.layui-header a:hover{transition:all .5s;-webkit-transition:all .5s}.layui-side{position:fixed;left:0;top:0;bottom:0;z-index:999;width:200px;overflow-x:hidden}.layui-side-scroll{position:relative;width:220px;height:100%;overflow-x:hidden}.layui-body{position:absolute;left:200px;right:0;top:0;bottom:0;z-index:998;width:auto;overflow:hidden;overflow-y:auto;box-sizing:border-box}.layui-layout-body{overflow:hidden}.layui-layout-admin .layui-header{background-color:#23262E}.layui-layout-admin .layui-side{top:60px;width:200px;overflow-x:hidden}.layui-layout-admin .layui-body{top:60px;bottom:44px}.layui-layout-admin .layui-main{width:auto;margin:0 15px}.layui-layout-admin .layui-footer{position:fixed;left:200px;right:0;bottom:0;height:44px;line-height:44px;padding:0 15px;background-color:#eee}.layui-layout-admin .layui-logo{position:absolute;left:0;top:0;width:200px;height:100%;line-height:60px;text-align:center;color:#009688;font-size:16px}.layui-layout-admin .layui-header .layui-nav{background:0 0}.layui-layout-left{position:absolute!important;left:200px;top:0}.layui-layout-right{position:absolute!important;right:0;top:0}.layui-container{position:relative;margin:0 auto;padding:0 15px;box-sizing:border-box}.layui-fluid{position:relative;margin:0 auto;padding:0 15px}.layui-row:after,.layui-row:before{content:'';display:block;clear:both}.layui-col-lg1,.layui-col-lg10,.layui-col-lg11,.layui-col-lg12,.layui-col-lg2,.layui-col-lg3,.layui-col-lg4,.layui-col-lg5,.layui-col-lg6,.layui-col-lg7,.layui-col-lg8,.layui-col-lg9,.layui-col-md1,.layui-col-md10,.layui-col-md11,.layui-col-md12,.layui-col-md2,.layui-col-md3,.layui-col-md4,.layui-col-md5,.layui-col-md6,.layui-col-md7,.layui-col-md8,.layui-col-md9,.layui-col-sm1,.layui-col-sm10,.layui-col-sm11,.layui-col-sm12,.layui-col-sm2,.layui-col-sm3,.layui-col-sm4,.layui-col-sm5,.layui-col-sm6,.layui-col-sm7,.layui-col-sm8,.layui-col-sm9,.layui-col-xs1,.layui-col-xs10,.layui-col-xs11,.layui-col-xs12,.layui-col-xs2,.layui-col-xs3,.layui-col-xs4,.layui-col-xs5,.layui-col-xs6,.layui-col-xs7,.layui-col-xs8,.layui-col-xs9{position:relative;display:block;box-sizing:border-box}.layui-col-xs1,.layui-col-xs10,.layui-col-xs11,.layui-col-xs12,.layui-col-xs2,.layui-col-xs3,.layui-col-xs4,.layui-col-xs5,.layui-col-xs6,.layui-col-xs7,.layui-col-xs8,.layui-col-xs9{float:left}.layui-col-xs1{width:8.33333333%}.layui-col-xs2{width:16.66666667%}.layui-col-xs3{width:25%}.layui-col-xs4{width:33.33333333%}.layui-col-xs5{width:41.66666667%}.layui-col-xs6{width:50%}.layui-col-xs7{width:58.33333333%}.layui-col-xs8{width:66.66666667%}.layui-col-xs9{width:75%}.layui-col-xs10{width:83.33333333%}.layui-col-xs11{width:91.66666667%}.layui-col-xs12{width:100%}.layui-col-xs-offset1{margin-left:8.33333333%}.layui-col-xs-offset2{margin-left:16.66666667%}.layui-col-xs-offset3{margin-left:25%}.layui-col-xs-offset4{margin-left:33.33333333%}.layui-col-xs-offset5{margin-left:41.66666667%}.layui-col-xs-offset6{margin-left:50%}.layui-col-xs-offset7{margin-left:58.33333333%}.layui-col-xs-offset8{margin-left:66.66666667%}.layui-col-xs-offset9{margin-left:75%}.layui-col-xs-offset10{margin-left:83.33333333%}.layui-col-xs-offset11{margin-left:91.66666667%}.layui-col-xs-offset12{margin-left:100%}@media screen and (max-width:768px){.layui-hide-xs{display:none!important}.layui-show-xs-block{display:block!important}.layui-show-xs-inline{display:inline!important}.layui-show-xs-inline-block{display:inline-block!important}}@media screen and (min-width:768px){.layui-container{width:750px}.layui-hide-sm{display:none!important}.layui-show-sm-block{display:block!important}.layui-show-sm-inline{display:inline!important}.layui-show-sm-inline-block{display:inline-block!important}.layui-col-sm1,.layui-col-sm10,.layui-col-sm11,.layui-col-sm12,.layui-col-sm2,.layui-col-sm3,.layui-col-sm4,.layui-col-sm5,.layui-col-sm6,.layui-col-sm7,.layui-col-sm8,.layui-col-sm9{float:left}.layui-col-sm1{width:8.33333333%}.layui-col-sm2{width:16.66666667%}.layui-col-sm3{width:25%}.layui-col-sm4{width:33.33333333%}.layui-col-sm5{width:41.66666667%}.layui-col-sm6{width:50%}.layui-col-sm7{width:58.33333333%}.layui-col-sm8{width:66.66666667%}.layui-col-sm9{width:75%}.layui-col-sm10{width:83.33333333%}.layui-col-sm11{width:91.66666667%}.layui-col-sm12{width:100%}.layui-col-sm-offset1{margin-left:8.33333333%}.layui-col-sm-offset2{margin-left:16.66666667%}.layui-col-sm-offset3{margin-left:25%}.layui-col-sm-offset4{margin-left:33.33333333%}.layui-col-sm-offset5{margin-left:41.66666667%}.layui-col-sm-offset6{margin-left:50%}.layui-col-sm-offset7{margin-left:58.33333333%}.layui-col-sm-offset8{margin-left:66.66666667%}.layui-col-sm-offset9{margin-left:75%}.layui-col-sm-offset10{margin-left:83.33333333%}.layui-col-sm-offset11{margin-left:91.66666667%}.layui-col-sm-offset12{margin-left:100%}}@media screen and (min-width:992px){.layui-container{width:970px}.layui-hide-md{display:none!important}.layui-show-md-block{display:block!important}.layui-show-md-inline{display:inline!important}.layui-show-md-inline-block{display:inline-block!important}.layui-col-md1,.layui-col-md10,.layui-col-md11,.layui-col-md12,.layui-col-md2,.layui-col-md3,.layui-col-md4,.layui-col-md5,.layui-col-md6,.layui-col-md7,.layui-col-md8,.layui-col-md9{float:left}.layui-col-md1{width:8.33333333%}.layui-col-md2{width:16.66666667%}.layui-col-md3{width:25%}.layui-col-md4{width:33.33333333%}.layui-col-md5{width:41.66666667%}.layui-col-md6{width:50%}.layui-col-md7{width:58.33333333%}.layui-col-md8{width:66.66666667%}.layui-col-md9{width:75%}.layui-col-md10{width:83.33333333%}.layui-col-md11{width:91.66666667%}.layui-col-md12{width:100%}.layui-col-md-offset1{margin-left:8.33333333%}.layui-col-md-offset2{margin-left:16.66666667%}.layui-col-md-offset3{margin-left:25%}.layui-col-md-offset4{margin-left:33.33333333%}.layui-col-md-offset5{margin-left:41.66666667%}.layui-col-md-offset6{margin-left:50%}.layui-col-md-offset7{margin-left:58.33333333%}.layui-col-md-offset8{margin-left:66.66666667%}.layui-col-md-offset9{margin-left:75%}.layui-col-md-offset10{margin-left:83.33333333%}.layui-col-md-offset11{margin-left:91.66666667%}.layui-col-md-offset12{margin-left:100%}}@media screen and (min-width:1200px){.layui-container{width:1170px}.layui-hide-lg{display:none!important}.layui-show-lg-block{display:block!important}.layui-show-lg-inline{display:inline!important}.layui-show-lg-inline-block{display:inline-block!important}.layui-col-lg1,.layui-col-lg10,.layui-col-lg11,.layui-col-lg12,.layui-col-lg2,.layui-col-lg3,.layui-col-lg4,.layui-col-lg5,.layui-col-lg6,.layui-col-lg7,.layui-col-lg8,.layui-col-lg9{float:left}.layui-col-lg1{width:8.33333333%}.layui-col-lg2{width:16.66666667%}.layui-col-lg3{width:25%}.layui-col-lg4{width:33.33333333%}.layui-col-lg5{width:41.66666667%}.layui-col-lg6{width:50%}.layui-col-lg7{width:58.33333333%}.layui-col-lg8{width:66.66666667%}.layui-col-lg9{width:75%}.layui-col-lg10{width:83.33333333%}.layui-col-lg11{width:91.66666667%}.layui-col-lg12{width:100%}.layui-col-lg-offset1{margin-left:8.33333333%}.layui-col-lg-offset2{margin-left:16.66666667%}.layui-col-lg-offset3{margin-left:25%}.layui-col-lg-offset4{margin-left:33.33333333%}.layui-col-lg-offset5{margin-left:41.66666667%}.layui-col-lg-offset6{margin-left:50%}.layui-col-lg-offset7{margin-left:58.33333333%}.layui-col-lg-offset8{margin-left:66.66666667%}.layui-col-lg-offset9{margin-left:75%}.layui-col-lg-offset10{margin-left:83.33333333%}.layui-col-lg-offset11{margin-left:91.66666667%}.layui-col-lg-offset12{margin-left:100%}}.layui-col-space1{margin:-.5px}.layui-col-space1>*{padding:.5px}.layui-col-space3{margin:-1.5px}.layui-col-space3>*{padding:1.5px}.layui-col-space5{margin:-2.5px}.layui-col-space5>*{padding:2.5px}.layui-col-space8{margin:-3.5px}.layui-col-space8>*{padding:3.5px}.layui-col-space10{margin:-5px}.layui-col-space10>*{padding:5px}.layui-col-space12{margin:-6px}.layui-col-space12>*{padding:6px}.layui-col-space15{margin:-7.5px}.layui-col-space15>*{padding:7.5px}.layui-col-space18{margin:-9px}.layui-col-space18>*{padding:9px}.layui-col-space20{margin:-10px}.layui-col-space20>*{padding:10px}.layui-col-space22{margin:-11px}.layui-col-space22>*{padding:11px}.layui-col-space25{margin:-12.5px}.layui-col-space25>*{padding:12.5px}.layui-col-space30{margin:-15px}.layui-col-space30>*{padding:15px}.layui-btn,.layui-input,.layui-select,.layui-textarea,.layui-upload-button{outline:0;-webkit-appearance:none;transition:all .3s;-webkit-transition:all .3s;box-sizing:border-box}.layui-elem-quote{margin-bottom:10px;padding:15px;line-height:22px;border-left:5px solid #009688;border-radius:0 2px 2px 0;background-color:#f2f2f2}.layui-quote-nm{border-style:solid;border-width:1px 1px 1px 5px;background:0 0}.layui-elem-field{margin-bottom:10px;padding:0;border-width:1px;border-style:solid}.layui-elem-field legend{margin-left:20px;padding:0 10px;font-size:20px;font-weight:300}.layui-field-title{margin:10px 0 20px;border-width:1px 0 0}.layui-field-box{padding:10px 15px}.layui-field-title .layui-field-box{padding:10px 0}.layui-progress{position:relative;height:6px;border-radius:20px;background-color:#e2e2e2}.layui-progress-bar{position:absolute;left:0;top:0;width:0;max-width:100%;height:6px;border-radius:20px;text-align:right;background-color:#5FB878;transition:all .3s;-webkit-transition:all .3s}.layui-progress-big,.layui-progress-big .layui-progress-bar{height:18px;line-height:18px}.layui-progress-text{position:relative;top:-20px;line-height:18px;font-size:12px;color:#666}.layui-progress-big .layui-progress-text{position:static;padding:0 10px;color:#fff}.layui-collapse{border-width:1px;border-style:solid;border-radius:2px}.layui-colla-content,.layui-colla-item{border-top-width:1px;border-top-style:solid}.layui-colla-item:first-child{border-top:none}.layui-colla-title{position:relative;height:42px;line-height:42px;padding:0 15px 0 35px;color:#333;background-color:#f2f2f2;cursor:pointer;font-size:14px;overflow:hidden}.layui-colla-content{display:none;padding:10px 15px;line-height:22px;color:#666}.layui-colla-icon{position:absolute;left:15px;top:0;font-size:14px}.layui-card-body,.layui-card-header,.layui-form-label,.layui-form-mid,.layui-form-select,.layui-input-block,.layui-input-inline,.layui-textarea{position:relative}.layui-card{margin-bottom:15px;border-radius:2px;background-color:#fff;box-shadow:0 1px 2px 0 rgba(0,0,0,.05)}.layui-card:last-child{margin-bottom:0}.layui-card-header{height:42px;line-height:42px;padding:0 15px;border-bottom:1px solid #f6f6f6;color:#333;border-radius:2px 2px 0 0;font-size:14px}.layui-bg-black,.layui-bg-blue,.layui-bg-cyan,.layui-bg-green,.layui-bg-orange,.layui-bg-red{color:#fff!important}.layui-card-body{padding:10px 15px;line-height:24px}.layui-card-body[pad15]{padding:15px}.layui-card-body[pad20]{padding:20px}.layui-card-body .layui-table{margin:5px 0}.layui-card .layui-tab{margin:0}.layui-panel-window{position:relative;padding:15px;border-radius:0;border-top:5px solid #E6E6E6;background-color:#fff}.layui-bg-red{background-color:#FF5722!important}.layui-bg-orange{background-color:#FFB800!important}.layui-bg-green{background-color:#009688!important}.layui-bg-cyan{background-color:#2F4056!important}.layui-bg-blue{background-color:#1E9FFF!important}.layui-bg-black{background-color:#393D49!important}.layui-bg-gray{background-color:#eee!important;color:#666!important}.layui-badge-rim,.layui-colla-content,.layui-colla-item,.layui-collapse,.layui-elem-field,.layui-form-pane .layui-form-item[pane],.layui-form-pane .layui-form-label,.layui-input,.layui-layedit,.layui-layedit-tool,.layui-quote-nm,.layui-select,.layui-tab-bar,.layui-tab-card,.layui-tab-title,.layui-tab-title .layui-this:after,.layui-textarea{border-color:#e6e6e6}.layui-timeline-item:before,hr{background-color:#e6e6e6}.layui-text{line-height:22px;font-size:14px;color:#666}.layui-text h1,.layui-text h2,.layui-text h3{font-weight:500;color:#333}.layui-text h1{font-size:30px}.layui-text h2{font-size:24px}.layui-text h3{font-size:18px}.layui-text a:not(.layui-btn){color:#01AAED}.layui-text a:not(.layui-btn):hover{text-decoration:underline}.layui-text ul{padding:5px 0 5px 15px}.layui-text ul li{margin-top:5px;list-style-type:disc}.layui-text em,.layui-word-aux{color:#999!important;padding:0 5px!important}.layui-btn{display:inline-block;height:38px;line-height:38px;padding:0 18px;background-color:#009688;color:#fff;white-space:nowrap;text-align:center;font-size:14px;border:none;border-radius:2px;cursor:pointer}.layui-btn:hover{opacity:.8;filter:alpha(opacity=80);color:#fff}.layui-btn:active{opacity:1;filter:alpha(opacity=100)}.layui-btn+.layui-btn{margin-left:10px}.layui-btn-container{font-size:0}.layui-btn-container .layui-btn{margin-right:10px;margin-bottom:10px}.layui-btn-container .layui-btn+.layui-btn{margin-left:0}.layui-table .layui-btn-container .layui-btn{margin-bottom:9px}.layui-btn-radius{border-radius:100px}.layui-btn .layui-icon{margin-right:3px;font-size:18px;vertical-align:bottom;vertical-align:middle\9}.layui-btn-primary{border:1px solid #C9C9C9;background-color:#fff;color:#555}.layui-btn-primary:hover{border-color:#009688;color:#333}.layui-btn-normal{background-color:#1E9FFF}.layui-btn-warm{background-color:#FFB800}.layui-btn-danger{background-color:#FF5722}.layui-btn-disabled,.layui-btn-disabled:active,.layui-btn-disabled:hover{border:1px solid #e6e6e6;background-color:#FBFBFB;color:#C9C9C9;cursor:not-allowed;opacity:1}.layui-btn-lg{height:44px;line-height:44px;padding:0 25px;font-size:16px}.layui-btn-sm{height:30px;line-height:30px;padding:0 10px;font-size:12px}.layui-btn-sm i{font-size:16px!important}.layui-btn-xs{height:22px;line-height:22px;padding:0 5px;font-size:12px}.layui-btn-xs i{font-size:14px!important}.layui-btn-group{display:inline-block;vertical-align:middle;font-size:0}.layui-btn-group .layui-btn{margin-left:0!important;margin-right:0!important;border-left:1px solid rgba(255,255,255,.5);border-radius:0}.layui-btn-group .layui-btn-primary{border-left:none}.layui-btn-group .layui-btn-primary:hover{border-color:#C9C9C9;color:#009688}.layui-btn-group .layui-btn:first-child{border-left:none;border-radius:2px 0 0 2px}.layui-btn-group .layui-btn-primary:first-child{border-left:1px solid #c9c9c9}.layui-btn-group .layui-btn:last-child{border-radius:0 2px 2px 0}.layui-btn-group .layui-btn+.layui-btn{margin-left:0}.layui-btn-group+.layui-btn-group{margin-left:10px}.layui-btn-fluid{width:100%}.layui-input,.layui-select,.layui-textarea{height:38px;line-height:1.3;line-height:38px\9;border-width:1px;border-style:solid;background-color:#fff;border-radius:2px}.layui-input::-webkit-input-placeholder,.layui-select::-webkit-input-placeholder,.layui-textarea::-webkit-input-placeholder{line-height:1.3}.layui-input,.layui-textarea{display:block;width:100%;padding-left:10px}.layui-input:hover,.layui-textarea:hover{border-color:#D2D2D2!important}.layui-input:focus,.layui-textarea:focus{border-color:#C9C9C9!important}.layui-textarea{min-height:100px;height:auto;line-height:20px;padding:6px 10px;resize:vertical}.layui-select{padding:0 10px}.layui-form input[type=checkbox],.layui-form input[type=radio],.layui-form select{display:none}.layui-form [lay-ignore]{display:initial}.layui-form-item{margin-bottom:15px;clear:both;*zoom:1}.layui-form-item:after{content:'\20';clear:both;*zoom:1;display:block;height:0}.layui-form-label{float:left;display:block;padding:9px 15px;width:80px;font-weight:400;line-height:20px;text-align:right}.layui-form-label-col{display:block;float:none;padding:9px 0;line-height:20px;text-align:left}.layui-form-item .layui-inline{margin-bottom:5px;margin-right:10px}.layui-input-block{margin-left:110px;min-height:36px}.layui-input-inline{display:inline-block;vertical-align:middle}.layui-form-item .layui-input-inline{float:left;width:190px;margin-right:10px}.layui-form-text .layui-input-inline{width:auto}.layui-form-mid{float:left;display:block;padding:9px 0!important;line-height:20px;margin-right:10px}.layui-form-danger+.layui-form-select .layui-input,.layui-form-danger:focus{border-color:#FF5722!important}.layui-form-select .layui-input{padding-right:30px;cursor:pointer}.layui-form-select .layui-edge{position:absolute;right:10px;top:50%;margin-top:-3px;cursor:pointer;border-width:6px;border-top-color:#c2c2c2;border-top-style:solid;transition:all .3s;-webkit-transition:all .3s}.layui-form-select dl{display:none;position:absolute;left:0;top:42px;padding:5px 0;z-index:999;min-width:100%;border:1px solid #d2d2d2;max-height:300px;overflow-y:auto;background-color:#fff;border-radius:2px;box-shadow:0 2px 4px rgba(0,0,0,.12);box-sizing:border-box}.layui-form-select dl dd,.layui-form-select dl dt{padding:0 10px;line-height:36px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.layui-form-select dl dt{font-size:12px;color:#999}.layui-form-select dl dd{cursor:pointer}.layui-form-select dl dd:hover{background-color:#f2f2f2}.layui-form-select .layui-select-group dd{padding-left:20px}.layui-form-select dl dd.layui-select-tips{padding-left:10px!important;color:#999}.layui-form-select dl dd.layui-this{background-color:#5FB878;color:#fff}.layui-form-checkbox,.layui-form-select dl dd.layui-disabled{background-color:#fff}.layui-form-selected dl{display:block}.layui-form-checkbox,.layui-form-checkbox *,.layui-form-switch{display:inline-block;vertical-align:middle}.layui-form-selected .layui-edge{margin-top:-9px;-webkit-transform:rotate(180deg);transform:rotate(180deg);margin-top:-3px\9}:root .layui-form-selected .layui-edge{margin-top:-9px\0/IE9}.layui-form-selectup dl{top:auto;bottom:42px}.layui-select-none{margin:5px 0;text-align:center;color:#999}.layui-select-disabled .layui-disabled{border-color:#eee!important}.layui-select-disabled .layui-edge{border-top-color:#d2d2d2}.layui-form-checkbox{position:relative;height:30px;line-height:30px;margin-right:10px;padding-right:30px;cursor:pointer;font-size:0;-webkit-transition:.1s linear;transition:.1s linear;box-sizing:border-box}.layui-form-checkbox span{padding:0 10px;height:100%;font-size:14px;border-radius:2px 0 0 2px;background-color:#d2d2d2;color:#fff;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.layui-form-checkbox:hover span{background-color:#c2c2c2}.layui-form-checkbox i{position:absolute;right:0;top:0;width:30px;height:28px;border:1px solid #d2d2d2;border-left:none;border-radius:0 2px 2px 0;color:#fff;font-size:20px;text-align:center}.layui-form-checkbox:hover i{border-color:#c2c2c2;color:#c2c2c2}.layui-form-checked,.layui-form-checked:hover{border-color:#5FB878}.layui-form-checked span,.layui-form-checked:hover span{background-color:#5FB878}.layui-form-checked i,.layui-form-checked:hover i{color:#5FB878}.layui-form-item .layui-form-checkbox{margin-top:4px}.layui-form-checkbox[lay-skin=primary]{height:auto!important;line-height:normal!important;border:none!important;margin-right:0;padding-right:0;background:0 0}.layui-form-checkbox[lay-skin=primary] span{float:right;padding-right:15px;line-height:18px;background:0 0;color:#666}.layui-form-checkbox[lay-skin=primary] i{position:relative;top:0;width:16px;height:16px;line-height:16px;border:1px solid #d2d2d2;font-size:12px;border-radius:2px;background-color:#fff;-webkit-transition:.1s linear;transition:.1s linear}.layui-form-checkbox[lay-skin=primary]:hover i{border-color:#5FB878;color:#fff}.layui-form-checked[lay-skin=primary] i{border-color:#5FB878;background-color:#5FB878;color:#fff}.layui-checkbox-disbaled[lay-skin=primary] span{background:0 0!important;color:#c2c2c2}.layui-checkbox-disbaled[lay-skin=primary]:hover i{border-color:#d2d2d2}.layui-form-item .layui-form-checkbox[lay-skin=primary]{margin-top:10px}.layui-form-switch{position:relative;height:22px;line-height:22px;min-width:35px;padding:0 5px;margin-top:8px;border:1px solid #d2d2d2;border-radius:20px;cursor:pointer;background-color:#fff;-webkit-transition:.1s linear;transition:.1s linear}.layui-form-switch i{position:absolute;left:5px;top:3px;width:16px;height:16px;border-radius:20px;background-color:#d2d2d2;-webkit-transition:.1s linear;transition:.1s linear}.layui-form-switch em{position:relative;top:0;width:25px;margin-left:21px;padding:0!important;text-align:center!important;color:#999!important;font-style:normal!important;font-size:12px}.layui-form-onswitch{border-color:#5FB878;background-color:#5FB878}.layui-checkbox-disbaled,.layui-checkbox-disbaled i{border-color:#e2e2e2!important}.layui-form-onswitch i{left:100%;margin-left:-21px;background-color:#fff}.layui-form-onswitch em{margin-left:5px;margin-right:21px;color:#fff!important}.layui-checkbox-disbaled span{background-color:#e2e2e2!important}.layui-checkbox-disbaled:hover i{color:#fff!important}[lay-radio]{display:none}.layui-form-radio,.layui-form-radio *{display:inline-block;vertical-align:middle}.layui-form-radio{line-height:28px;margin:6px 10px 0 0;padding-right:10px;cursor:pointer;font-size:0}.layui-form-radio *{font-size:14px}.layui-form-radio>i{margin-right:8px;font-size:22px;color:#c2c2c2}.layui-form-radio>i:hover,.layui-form-radioed>i{color:#5FB878}.layui-radio-disbaled>i{color:#e2e2e2!important}.layui-form-pane .layui-form-label{width:110px;padding:8px 15px;height:38px;line-height:20px;border-width:1px;border-style:solid;border-radius:2px 0 0 2px;text-align:center;background-color:#FBFBFB;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;box-sizing:border-box}.layui-form-pane .layui-input-inline{margin-left:-1px}.layui-form-pane .layui-input-block{margin-left:110px;left:-1px}.layui-form-pane .layui-input{border-radius:0 2px 2px 0}.layui-form-pane .layui-form-text .layui-form-label{float:none;width:100%;border-radius:2px;box-sizing:border-box;text-align:left}.layui-form-pane .layui-form-text .layui-input-inline{display:block;margin:0;top:-1px;clear:both}.layui-form-pane .layui-form-text .layui-input-block{margin:0;left:0;top:-1px}.layui-form-pane .layui-form-text .layui-textarea{min-height:100px;border-radius:0 0 2px 2px}.layui-form-pane .layui-form-checkbox{margin:4px 0 4px 10px}.layui-form-pane .layui-form-radio,.layui-form-pane .layui-form-switch{margin-top:6px;margin-left:10px}.layui-form-pane .layui-form-item[pane]{position:relative;border-width:1px;border-style:solid}.layui-form-pane .layui-form-item[pane] .layui-form-label{position:absolute;left:0;top:0;height:100%;border-width:0 1px 0 0}.layui-form-pane .layui-form-item[pane] .layui-input-inline{margin-left:110px}@media screen and (max-width:450px){.layui-form-item .layui-form-label{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-form-item .layui-inline{display:block;margin-right:0;margin-bottom:20px;clear:both}.layui-form-item .layui-inline:after{content:'\20';clear:both;display:block;height:0}.layui-form-item .layui-input-inline{display:block;float:none;left:-3px;width:auto;margin:0 0 10px 112px}.layui-form-item .layui-input-inline+.layui-form-mid{margin-left:110px;top:-5px;padding:0}.layui-form-item .layui-form-checkbox{margin-right:5px;margin-bottom:5px}}.layui-layedit{border-width:1px;border-style:solid;border-radius:2px}.layui-layedit-tool{padding:3px 5px;border-bottom-width:1px;border-bottom-style:solid;font-size:0}.layedit-tool-fixed{position:fixed;top:0;border-top:1px solid #e2e2e2}.layui-layedit-tool .layedit-tool-mid,.layui-layedit-tool .layui-icon{display:inline-block;vertical-align:middle;text-align:center;font-size:14px}.layui-layedit-tool .layui-icon{position:relative;width:32px;height:30px;line-height:30px;margin:3px 5px;color:#777;cursor:pointer;border-radius:2px}.layui-layedit-tool .layui-icon:hover{color:#393D49}.layui-layedit-tool .layui-icon:active{color:#000}.layui-layedit-tool .layedit-tool-active{background-color:#e2e2e2;color:#000}.layui-layedit-tool .layui-disabled,.layui-layedit-tool .layui-disabled:hover{color:#d2d2d2;cursor:not-allowed}.layui-layedit-tool .layedit-tool-mid{width:1px;height:18px;margin:0 10px;background-color:#d2d2d2}.layedit-tool-html{width:50px!important;font-size:30px!important}.layedit-tool-b,.layedit-tool-code,.layedit-tool-help{font-size:16px!important}.layedit-tool-d,.layedit-tool-face,.layedit-tool-image,.layedit-tool-unlink{font-size:18px!important}.layedit-tool-image input{position:absolute;font-size:0;left:0;top:0;width:100%;height:100%;opacity:.01;filter:Alpha(opacity=1);cursor:pointer}.layui-layedit-iframe iframe{display:block;width:100%}#LAY_layedit_code{overflow:hidden}.layui-laypage{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;margin:10px 0;font-size:0}.layui-laypage>a:first-child,.layui-laypage>a:first-child em{border-radius:2px 0 0 2px}.layui-laypage>a:last-child,.layui-laypage>a:last-child em{border-radius:0 2px 2px 0}.layui-laypage>:first-child{margin-left:0!important}.layui-laypage>:last-child{margin-right:0!important}.layui-laypage a,.layui-laypage button,.layui-laypage input,.layui-laypage select,.layui-laypage span{border:1px solid #e2e2e2}.layui-laypage a,.layui-laypage span{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;padding:0 15px;height:28px;line-height:28px;margin:0 -1px 5px 0;background-color:#fff;color:#333;font-size:12px}.layui-laypage a:hover{color:#009688}.layui-laypage em{font-style:normal}.layui-laypage .layui-laypage-spr{color:#999;font-weight:700}.layui-laypage a{text-decoration:none}.layui-laypage .layui-laypage-curr{position:relative}.layui-laypage .layui-laypage-curr em{position:relative;color:#fff}.layui-laypage .layui-laypage-curr .layui-laypage-em{position:absolute;left:-1px;top:-1px;padding:1px;width:100%;height:100%;background-color:#009688}.layui-laypage-em{border-radius:2px}.layui-laypage-next em,.layui-laypage-prev em{font-family:Sim sun;font-size:16px}.layui-laypage .layui-laypage-count,.layui-laypage .layui-laypage-limits,.layui-laypage .layui-laypage-refresh,.layui-laypage .layui-laypage-skip{margin-left:10px;margin-right:10px;padding:0;border:none}.layui-laypage .layui-laypage-limits,.layui-laypage .layui-laypage-refresh{vertical-align:top}.layui-laypage .layui-laypage-refresh i{font-size:18px;cursor:pointer}.layui-laypage select{height:22px;padding:3px;border-radius:2px;cursor:pointer}.layui-laypage .layui-laypage-skip{height:30px;line-height:30px;color:#999}.layui-laypage button,.layui-laypage input{height:30px;line-height:30px;border-radius:2px;vertical-align:top;background-color:#fff;box-sizing:border-box}.layui-laypage input{display:inline-block;width:40px;margin:0 10px;padding:0 3px;text-align:center}.layui-laypage input:focus,.layui-laypage select:focus{border-color:#009688!important}.layui-laypage button{margin-left:10px;padding:0 10px;cursor:pointer}.layui-table,.layui-table-view{margin:10px 0}.layui-flow-more{margin:10px 0;text-align:center;color:#999;font-size:14px}.layui-flow-more a{height:32px;line-height:32px}.layui-flow-more a *{display:inline-block;vertical-align:top}.layui-flow-more a cite{padding:0 20px;border-radius:3px;background-color:#eee;color:#333;font-style:normal}.layui-flow-more a cite:hover{opacity:.8}.layui-flow-more a i{font-size:30px;color:#737383}.layui-table{width:100%;background-color:#fff;color:#666}.layui-table tr{transition:all .3s;-webkit-transition:all .3s}.layui-table th{text-align:left;font-weight:400}.layui-table tbody tr:hover,.layui-table thead tr,.layui-table-click,.layui-table-header,.layui-table-hover,.layui-table-mend,.layui-table-patch,.layui-table-tool,.layui-table[lay-even] tr:nth-child(even){background-color:#f2f2f2}.layui-table td,.layui-table th,.layui-table-fixed-r,.layui-table-header,.layui-table-page,.layui-table-tips-main,.layui-table-tool,.layui-table-view,.layui-table[lay-skin=line],.layui-table[lay-skin=row]{border-width:1px;border-style:solid;border-color:#e6e6e6}.layui-table td,.layui-table th{position:relative;padding:9px 15px;min-height:20px;line-height:20px;font-size:14px}.layui-table[lay-skin=line] td,.layui-table[lay-skin=line] th{border-width:0 0 1px}.layui-table[lay-skin=row] td,.layui-table[lay-skin=row] th{border-width:0 1px 0 0}.layui-table[lay-skin=nob] td,.layui-table[lay-skin=nob] th{border:none}.layui-table img{max-width:100px}.layui-table[lay-size=lg] td,.layui-table[lay-size=lg] th{padding:15px 30px}.layui-table-view .layui-table[lay-size=lg] .layui-table-cell{height:40px;line-height:40px}.layui-table[lay-size=sm] td,.layui-table[lay-size=sm] th{font-size:12px;padding:5px 10px}.layui-table-view .layui-table[lay-size=sm] .layui-table-cell{height:20px;line-height:20px}.layui-table[lay-data]{display:none}.layui-table-box,.layui-table-view{position:relative;overflow:hidden}.layui-table-view .layui-table{position:relative;width:auto;margin:0}.layui-table-body,.layui-table-header .layui-table,.layui-table-page{margin-bottom:-1px}.layui-table-view .layui-table[lay-skin=line]{border-width:0 1px 0 0}.layui-table-view .layui-table[lay-skin=row]{border-width:0 0 1px}.layui-table-view .layui-table td,.layui-table-view .layui-table th{padding:5px 0;border-top:none;border-left:none}.layui-table-view .layui-table td{cursor:default}.layui-table-view .layui-form-checkbox[lay-skin=primary] i{width:18px;height:18px}.layui-table-header{border-width:0 0 1px;overflow:hidden}.layui-table-sort{width:10px;height:20px;margin-left:5px;cursor:pointer!important}.layui-table-sort .layui-edge{position:absolute;left:5px;border-width:5px}.layui-table-sort .layui-table-sort-asc{top:4px;border-top:none;border-bottom-style:solid;border-bottom-color:#b2b2b2}.layui-table-sort .layui-table-sort-asc:hover{border-bottom-color:#666}.layui-table-sort .layui-table-sort-desc{bottom:4px;border-bottom:none;border-top-style:solid;border-top-color:#b2b2b2}.layui-table-sort .layui-table-sort-desc:hover{border-top-color:#666}.layui-table-sort[lay-sort=asc] .layui-table-sort-asc{border-bottom-color:#000}.layui-table-sort[lay-sort=desc] .layui-table-sort-desc{border-top-color:#000}.layui-table-cell{height:28px;line-height:28px;padding:0 15px;position:relative;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;box-sizing:border-box}.layui-table-cell .layui-form-checkbox[lay-skin=primary],.layui-table-cell .layui-form-radio[lay-skin=primary]{top:-1px;vertical-align:middle}.layui-table-cell .layui-form-radio{padding-right:0}.layui-table-cell .layui-form-radio>i{margin-right:0}.layui-table-cell .layui-table-link{color:#01AAED}.laytable-cell-checkbox,.laytable-cell-numbers,.laytable-cell-radio,.laytable-cell-space{padding:0;text-align:center}.layui-table-body{position:relative;overflow:auto;margin-right:-1px}.layui-table-body .layui-none{line-height:40px;text-align:center;color:#999}.layui-table-fixed{position:absolute;left:0;top:0}.layui-table-fixed .layui-table-body{overflow:hidden}.layui-table-fixed-l{box-shadow:0 -1px 8px rgba(0,0,0,.08)}.layui-table-fixed-r{left:auto;right:-1px;border-width:0 0 0 1px;box-shadow:-1px 0 8px rgba(0,0,0,.08)}.layui-table-fixed-r .layui-table-header{position:relative;overflow:visible}.layui-table-mend{position:absolute;right:-49px;top:0;height:100%;width:50px}.layui-table-tool{position:relative;width:100%;height:50px;line-height:30px;padding:10px 15px;border-width:0 0 1px}.layui-table-page{position:relative;width:100%;padding:7px 7px 0;border-width:1px 0 0;height:41px;font-size:12px}.layui-table-page>div{height:26px}.layui-table-page .layui-laypage{margin:0}.layui-table-page .layui-laypage a,.layui-table-page .layui-laypage span{height:26px;line-height:26px;margin-bottom:10px;border:none;background:0 0}.layui-table-page .layui-laypage a,.layui-table-page .layui-laypage span.layui-laypage-curr{padding:0 12px}.layui-table-page .layui-laypage span{margin-left:0;padding:0}.layui-table-page .layui-laypage .layui-laypage-prev{margin-left:-7px!important}.layui-table-page .layui-laypage .layui-laypage-curr .layui-laypage-em{left:0;top:0;padding:0}.layui-table-page .layui-laypage button,.layui-table-page .layui-laypage input{height:26px;line-height:26px}.layui-table-page .layui-laypage input{width:40px}.layui-table-page .layui-laypage button{padding:0 10px}.layui-table-page select{height:18px}.layui-table-view select[lay-ignore]{display:inline-block}.layui-table-patch .layui-table-cell{padding:0;width:30px}.layui-table-edit{position:absolute;left:0;top:0;width:100%;height:100%;padding:0 14px 1px;border-radius:0;box-shadow:1px 1px 20px rgba(0,0,0,.15)}.layui-table-edit:focus{border-color:#5FB878!important}select.layui-table-edit{padding:0 0 0 10px;border-color:#C9C9C9}.layui-table-view .layui-form-checkbox,.layui-table-view .layui-form-radio,.layui-table-view .layui-form-switch{top:0;margin:0;box-sizing:content-box}.layui-table-view .layui-form-checkbox{top:-1px;height:26px;line-height:26px}body .layui-table-tips .layui-layer-content{background:0 0;padding:0;box-shadow:0 1px 6px rgba(0,0,0,.1)}.layui-table-tips-main{margin:-44px 0 0 -1px;max-height:150px;padding:8px 15px;font-size:14px;overflow-y:scroll;background-color:#fff;color:#333}.layui-table-tips-c{position:absolute;right:-3px;top:-12px;width:18px;height:18px;padding:3px;text-align:center;font-weight:700;border-radius:100%;font-size:14px;cursor:pointer;background-color:#666}.layui-table-tips-c:hover{background-color:#999}.layui-upload-file{display:none!important;opacity:.01;filter:Alpha(opacity=1)}.layui-upload-drag,.layui-upload-form,.layui-upload-wrap{display:inline-block}.layui-upload-list{margin:10px 0}.layui-upload-choose{padding:0 10px;color:#999}.layui-upload-drag{position:relative;padding:30px;border:1px dashed #e2e2e2;background-color:#fff;text-align:center;cursor:pointer;color:#999}.layui-upload-drag .layui-icon{font-size:50px;color:#009688}.layui-upload-drag[lay-over]{border-color:#009688}.layui-upload-iframe{position:absolute;width:0;height:0;border:0;visibility:hidden}.layui-upload-wrap{position:relative;vertical-align:middle}.layui-upload-wrap .layui-upload-file{display:block!important;position:absolute;left:0;top:0;z-index:10;font-size:100px;width:100%;height:100%;opacity:.01;filter:Alpha(opacity=1);cursor:pointer}.layui-rate,.layui-rate *{display:inline-block;vertical-align:middle}.layui-rate{padding:10px 5px 10px 0;font-size:0}.layui-rate li i.layui-icon{font-size:20px;color:#FFB800;margin-right:5px;transition:all .3s;-webkit-transition:all .3s}.layui-rate li i:hover{cursor:pointer;transform:scale(1.12);-webkit-transform:scale(1.12)}.layui-rate[readonly] li i:hover{cursor:default;transform:scale(1)}.layui-code{position:relative;margin:10px 0;padding:15px;line-height:20px;border:1px solid #ddd;border-left-width:6px;background-color:#F2F2F2;color:#333;font-family:Courier New;font-size:12px}.layui-tree{line-height:26px}.layui-tree li{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-tree li .layui-tree-spread,.layui-tree li a{display:inline-block;vertical-align:top;height:26px;*display:inline;*zoom:1;cursor:pointer}.layui-tree li a{font-size:0}.layui-tree li a i{font-size:16px}.layui-tree li a cite{padding:0 6px;font-size:14px;font-style:normal}.layui-tree li i{padding-left:6px;color:#333;-moz-user-select:none}.layui-tree li .layui-tree-check{font-size:13px}.layui-tree li .layui-tree-check:hover{color:#009E94}.layui-tree li ul{display:none;margin-left:20px}.layui-tree li .layui-tree-enter{line-height:24px;border:1px dotted #000}.layui-tree-drag{display:none;position:absolute;left:-666px;top:-666px;background-color:#f2f2f2;padding:5px 10px;border:1px dotted #000;white-space:nowrap}.layui-tree-drag i{padding-right:5px}.layui-nav{position:relative;padding:0 20px;background-color:#393D49;color:#fff;border-radius:2px;font-size:0;box-sizing:border-box}.layui-nav *{font-size:14px}.layui-nav .layui-nav-item{position:relative;display:inline-block;*display:inline;*zoom:1;vertical-align:middle;line-height:60px}.layui-nav .layui-nav-item a{display:block;padding:0 20px;color:#fff;color:rgba(255,255,255,.7);transition:all .3s;-webkit-transition:all .3s}.layui-nav .layui-this:after,.layui-nav-bar,.layui-nav-tree .layui-nav-itemed:after{position:absolute;left:0;top:0;width:0;height:5px;background-color:#5FB878;transition:all .2s;-webkit-transition:all .2s}.layui-nav-bar{z-index:1000}.layui-nav .layui-nav-item a:hover,.layui-nav .layui-this a{color:#fff}.layui-nav .layui-this:after{content:'';top:auto;bottom:0;width:100%}.layui-nav-img{width:30px;height:30px;margin-right:10px;border-radius:50%}.layui-nav .layui-nav-more{content:'';width:0;height:0;border-style:solid dashed dashed;border-color:#fff transparent transparent;overflow:hidden;cursor:pointer;transition:all .2s;-webkit-transition:all .2s;position:absolute;top:50%;right:3px;margin-top:-3px;border-width:6px;border-top-color:rgba(255,255,255,.7)}.layui-nav .layui-nav-mored,.layui-nav-itemed>a .layui-nav-more{margin-top:-9px;border-style:dashed dashed solid;border-color:transparent transparent #fff}.layui-nav-child{display:none;position:absolute;left:0;top:65px;min-width:100%;line-height:36px;padding:5px 0;box-shadow:0 2px 4px rgba(0,0,0,.12);border:1px solid #d2d2d2;background-color:#fff;z-index:100;border-radius:2px;white-space:nowrap}.layui-nav .layui-nav-child a{color:#333}.layui-nav .layui-nav-child a:hover{background-color:#f2f2f2;color:#000}.layui-nav-child dd{position:relative}.layui-nav .layui-nav-child dd.layui-this a,.layui-nav-child dd.layui-this{background-color:#5FB878;color:#fff}.layui-nav-child dd.layui-this:after{display:none}.layui-nav-tree{width:200px;padding:0}.layui-nav-tree .layui-nav-item{display:block;width:100%;line-height:45px}.layui-nav-tree .layui-nav-item a{position:relative;height:45px;line-height:45px;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-nav-tree .layui-nav-item a:hover{background-color:#4E5465}.layui-nav-tree .layui-nav-bar{width:5px;height:0;background-color:#009688}.layui-nav-tree .layui-nav-child dd.layui-this,.layui-nav-tree .layui-nav-child dd.layui-this a,.layui-nav-tree .layui-this,.layui-nav-tree .layui-this>a,.layui-nav-tree .layui-this>a:hover{background-color:#009688;color:#fff}.layui-nav-tree .layui-this:after{display:none}.layui-nav-itemed>a,.layui-nav-tree .layui-nav-title a,.layui-nav-tree .layui-nav-title a:hover{color:#fff!important}.layui-nav-tree .layui-nav-child{position:relative;z-index:0;top:0;border:none;box-shadow:none}.layui-nav-tree .layui-nav-child a{height:40px;line-height:40px;color:#fff;color:rgba(255,255,255,.7)}.layui-nav-tree .layui-nav-child,.layui-nav-tree .layui-nav-child a:hover{background:0 0;color:#fff}.layui-nav-tree .layui-nav-more{right:10px}.layui-nav-itemed>.layui-nav-child{display:block;padding:0;background-color:rgba(0,0,0,.3)!important}.layui-nav-itemed>.layui-nav-child>.layui-this>.layui-nav-child{display:block}.layui-nav-side{position:fixed;top:0;bottom:0;left:0;overflow-x:hidden;z-index:999}.layui-bg-blue .layui-nav-bar,.layui-bg-blue .layui-nav-itemed:after,.layui-bg-blue .layui-this:after{background-color:#93D1FF}.layui-bg-blue .layui-nav-child dd.layui-this{background-color:#1E9FFF}.layui-bg-blue .layui-nav-itemed>a,.layui-nav-tree.layui-bg-blue .layui-nav-title a,.layui-nav-tree.layui-bg-blue .layui-nav-title a:hover{background-color:#007DDB!important}.layui-breadcrumb{visibility:hidden;font-size:0}.layui-breadcrumb>*{font-size:14px}.layui-breadcrumb a{color:#999!important}.layui-breadcrumb a:hover{color:#5FB878!important}.layui-breadcrumb a cite{color:#666;font-style:normal}.layui-breadcrumb span[lay-separator]{margin:0 10px;color:#999}.layui-tab{margin:10px 0;text-align:left!important}.layui-tab[overflow]>.layui-tab-title{overflow:hidden}.layui-tab-title{position:relative;left:0;height:40px;white-space:nowrap;font-size:0;border-bottom-width:1px;border-bottom-style:solid;transition:all .2s;-webkit-transition:all .2s}.layui-tab-title li{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;font-size:14px;transition:all .2s;-webkit-transition:all .2s;position:relative;line-height:40px;min-width:65px;padding:0 15px;text-align:center;cursor:pointer}.layui-tab-title li a{display:block}.layui-tab-title .layui-this{color:#000}.layui-tab-title .layui-this:after{position:absolute;left:0;top:0;content:'';width:100%;height:41px;border-width:1px;border-style:solid;border-bottom-color:#fff;border-radius:2px 2px 0 0;box-sizing:border-box;pointer-events:none}.layui-tab-bar{position:absolute;right:0;top:0;z-index:10;width:30px;height:39px;line-height:39px;border-width:1px;border-style:solid;border-radius:2px;text-align:center;background-color:#fff;cursor:pointer}.layui-tab-bar .layui-icon{position:relative;display:inline-block;top:3px;transition:all .3s;-webkit-transition:all .3s}.layui-tab-item{display:none}.layui-tab-more{padding-right:30px;height:auto!important;white-space:normal!important}.layui-tab-more li.layui-this:after{border-bottom-color:#e2e2e2;border-radius:2px}.layui-tab-more .layui-tab-bar .layui-icon{top:-2px;top:3px\9;-webkit-transform:rotate(180deg);transform:rotate(180deg)}:root .layui-tab-more .layui-tab-bar .layui-icon{top:-2px\0/IE9}.layui-tab-content{padding:10px}.layui-tab-title li .layui-tab-close{position:relative;display:inline-block;width:18px;height:18px;line-height:20px;margin-left:8px;top:1px;text-align:center;font-size:14px;color:#c2c2c2;transition:all .2s;-webkit-transition:all .2s}.layui-tab-title li .layui-tab-close:hover{border-radius:2px;background-color:#FF5722;color:#fff}.layui-tab-brief>.layui-tab-title .layui-this{color:#009688}.layui-tab-brief>.layui-tab-more li.layui-this:after,.layui-tab-brief>.layui-tab-title .layui-this:after{border:none;border-radius:0;border-bottom:2px solid #5FB878}.layui-tab-brief[overflow]>.layui-tab-title .layui-this:after{top:-1px}.layui-tab-card{border-width:1px;border-style:solid;border-radius:2px;box-shadow:0 2px 5px 0 rgba(0,0,0,.1)}.layui-tab-card>.layui-tab-title{background-color:#f2f2f2}.layui-tab-card>.layui-tab-title li{margin-right:-1px;margin-left:-1px}.layui-tab-card>.layui-tab-title .layui-this{background-color:#fff}.layui-tab-card>.layui-tab-title .layui-this:after{border-top:none;border-width:1px;border-bottom-color:#fff}.layui-tab-card>.layui-tab-title .layui-tab-bar{height:40px;line-height:40px;border-radius:0;border-top:none;border-right:none}.layui-tab-card>.layui-tab-more .layui-this{background:0 0;color:#5FB878}.layui-tab-card>.layui-tab-more .layui-this:after{border:none}.layui-timeline{padding-left:5px}.layui-timeline-item{position:relative;padding-bottom:20px}.layui-timeline-axis{position:absolute;left:-5px;top:0;z-index:10;width:20px;height:20px;line-height:20px;background-color:#fff;color:#5FB878;border-radius:50%;text-align:center;cursor:pointer}.layui-timeline-axis:hover{color:#FF5722}.layui-timeline-item:before{content:'';position:absolute;left:5px;top:0;z-index:0;width:1px;height:100%}.layui-timeline-item:last-child:before{display:none}.layui-timeline-item:first-child:before{display:block}.layui-timeline-content{padding-left:25px}.layui-timeline-title{position:relative;margin-bottom:10px}.layui-badge,.layui-badge-dot,.layui-badge-rim{position:relative;display:inline-block;padding:0 6px;font-size:12px;text-align:center;background-color:#FF5722;color:#fff;border-radius:2px}.layui-badge{height:18px;line-height:18px}.layui-badge-dot{width:8px;height:8px;padding:0;border-radius:50%}.layui-badge-rim{height:18px;line-height:18px;border-width:1px;border-style:solid;background-color:#fff;color:#666}.layui-btn .layui-badge,.layui-btn .layui-badge-dot{margin-left:5px}.layui-nav .layui-badge,.layui-nav .layui-badge-dot{position:absolute;top:50%;margin:-8px 6px 0}.layui-tab-title .layui-badge,.layui-tab-title .layui-badge-dot{left:5px;top:-2px}.layui-carousel{position:relative;left:0;top:0;background-color:#f8f8f8}.layui-carousel>[carousel-item]{position:relative;width:100%;height:100%;overflow:hidden}.layui-carousel>[carousel-item]:before{position:absolute;content:'\e63d';left:50%;top:50%;width:100px;line-height:20px;margin:-10px 0 0 -50px;text-align:center;color:#c2c2c2;font-family:layui-icon!important;font-size:30px;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.layui-carousel>[carousel-item]>*{display:none;position:absolute;left:0;top:0;width:100%;height:100%;background-color:#f8f8f8;transition-duration:.3s;-webkit-transition-duration:.3s}.layui-carousel-updown>*{-webkit-transition:.3s ease-in-out up;transition:.3s ease-in-out up}.layui-carousel-arrow{display:none\9;opacity:0;position:absolute;left:10px;top:50%;margin-top:-18px;width:36px;height:36px;line-height:36px;text-align:center;font-size:20px;border:0;border-radius:50%;background-color:rgba(0,0,0,.2);color:#fff;-webkit-transition-duration:.3s;transition-duration:.3s;cursor:pointer}.layui-carousel-arrow[lay-type=add]{left:auto!important;right:10px}.layui-carousel:hover .layui-carousel-arrow[lay-type=add],.layui-carousel[lay-arrow=always] .layui-carousel-arrow[lay-type=add]{right:20px}.layui-carousel[lay-arrow=always] .layui-carousel-arrow{opacity:1;left:20px}.layui-carousel[lay-arrow=none] .layui-carousel-arrow{display:none}.layui-carousel-arrow:hover,.layui-carousel-ind ul:hover{background-color:rgba(0,0,0,.35)}.layui-carousel:hover .layui-carousel-arrow{display:block\9;opacity:1;left:20px}.layui-carousel-ind{position:relative;top:-35px;width:100%;line-height:0!important;text-align:center;font-size:0}.layui-carousel[lay-indicator=outside]{margin-bottom:30px}.layui-carousel[lay-indicator=outside] .layui-carousel-ind{top:10px}.layui-carousel[lay-indicator=outside] .layui-carousel-ind ul{background-color:rgba(0,0,0,.5)}.layui-carousel[lay-indicator=none] .layui-carousel-ind{display:none}.layui-carousel-ind ul{display:inline-block;padding:5px;background-color:rgba(0,0,0,.2);border-radius:10px;-webkit-transition-duration:.3s;transition-duration:.3s}.layui-carousel-ind li{display:inline-block;width:10px;height:10px;margin:0 3px;font-size:14px;background-color:#e2e2e2;background-color:rgba(255,255,255,.5);border-radius:50%;cursor:pointer;-webkit-transition-duration:.3s;transition-duration:.3s}.layui-carousel-ind li:hover{background-color:rgba(255,255,255,.7)}.layui-carousel-ind li.layui-this{background-color:#fff}.layui-carousel>[carousel-item]>.layui-carousel-next,.layui-carousel>[carousel-item]>.layui-carousel-prev,.layui-carousel>[carousel-item]>.layui-this{display:block}.layui-carousel>[carousel-item]>.layui-this{left:0}.layui-carousel>[carousel-item]>.layui-carousel-prev{left:-100%}.layui-carousel>[carousel-item]>.layui-carousel-next{left:100%}.layui-carousel>[carousel-item]>.layui-carousel-next.layui-carousel-left,.layui-carousel>[carousel-item]>.layui-carousel-prev.layui-carousel-right{left:0}.layui-carousel>[carousel-item]>.layui-this.layui-carousel-left{left:-100%}.layui-carousel>[carousel-item]>.layui-this.layui-carousel-right{left:100%}.layui-carousel[lay-anim=updown] .layui-carousel-arrow{left:50%!important;top:20px;margin:0 0 0 -18px}.layui-carousel[lay-anim=updown]>[carousel-item]>*,.layui-carousel[lay-anim=fade]>[carousel-item]>*{left:0!important}.layui-carousel[lay-anim=updown] .layui-carousel-arrow[lay-type=add]{top:auto!important;bottom:20px}.layui-carousel[lay-anim=updown] .layui-carousel-ind{position:absolute;top:50%;right:20px;width:auto;height:auto}.layui-carousel[lay-anim=updown] .layui-carousel-ind ul{padding:3px 5px}.layui-carousel[lay-anim=updown] .layui-carousel-ind li{display:block;margin:6px 0}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-this{top:0}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-carousel-prev{top:-100%}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-carousel-next{top:100%}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-carousel-next.layui-carousel-left,.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-carousel-prev.layui-carousel-right{top:0}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-this.layui-carousel-left{top:-100%}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-this.layui-carousel-right{top:100%}.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-carousel-next,.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-carousel-prev{opacity:0}.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-carousel-next.layui-carousel-left,.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-carousel-prev.layui-carousel-right{opacity:1}.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-this.layui-carousel-left,.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-this.layui-carousel-right{opacity:0}.layui-fixbar{position:fixed;right:15px;bottom:15px;z-index:9999}.layui-fixbar li{width:50px;height:50px;line-height:50px;margin-bottom:1px;text-align:center;cursor:pointer;font-size:30px;background-color:#9F9F9F;color:#fff;border-radius:2px;opacity:.95}.layui-fixbar li:hover{opacity:.85}.layui-fixbar li:active{opacity:1}.layui-fixbar .layui-fixbar-top{display:none;font-size:40px}body .layui-util-face{border:none;background:0 0}body .layui-util-face .layui-layer-content{padding:0;background-color:#fff;color:#666;box-shadow:none}.layui-util-face .layui-layer-TipsG{display:none}.layui-util-face ul{position:relative;width:372px;padding:10px;border:1px solid #D9D9D9;background-color:#fff;box-shadow:0 0 20px rgba(0,0,0,.2)}.layui-util-face ul li{cursor:pointer;float:left;border:1px solid #e8e8e8;height:22px;width:26px;overflow:hidden;margin:-1px 0 0 -1px;padding:4px 2px;text-align:center}.layui-util-face ul li:hover{position:relative;z-index:2;border:1px solid #eb7350;background:#fff9ec}.layui-anim{-webkit-animation-duration:.3s;animation-duration:.3s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.layui-anim.layui-icon{display:inline-block}.layui-anim-loop{-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.layui-trans,.layui-trans a{transition:all .3s;-webkit-transition:all .3s}@-webkit-keyframes layui-rotate{from{-webkit-transform:rotate(0)}to{-webkit-transform:rotate(360deg)}}@keyframes layui-rotate{from{transform:rotate(0)}to{transform:rotate(360deg)}}.layui-anim-rotate{-webkit-animation-name:layui-rotate;animation-name:layui-rotate;-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-timing-function:linear;animation-timing-function:linear}@-webkit-keyframes layui-up{from{-webkit-transform:translate3d(0,100%,0);opacity:.3}to{-webkit-transform:translate3d(0,0,0);opacity:1}}@keyframes layui-up{from{transform:translate3d(0,100%,0);opacity:.3}to{transform:translate3d(0,0,0);opacity:1}}.layui-anim-up{-webkit-animation-name:layui-up;animation-name:layui-up}@-webkit-keyframes layui-upbit{from{-webkit-transform:translate3d(0,30px,0);opacity:.3}to{-webkit-transform:translate3d(0,0,0);opacity:1}}@keyframes layui-upbit{from{transform:translate3d(0,30px,0);opacity:.3}to{transform:translate3d(0,0,0);opacity:1}}.layui-anim-upbit{-webkit-animation-name:layui-upbit;animation-name:layui-upbit}@-webkit-keyframes layui-scale{0%{opacity:.3;-webkit-transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1)}}@keyframes layui-scale{0%{opacity:.3;-ms-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-ms-transform:scale(1);transform:scale(1)}}.layui-anim-scale{-webkit-animation-name:layui-scale;animation-name:layui-scale}@-webkit-keyframes layui-scale-spring{0%{opacity:.5;-webkit-transform:scale(.5)}80%{opacity:.8;-webkit-transform:scale(1.1)}100%{opacity:1;-webkit-transform:scale(1)}}@keyframes layui-scale-spring{0%{opacity:.5;transform:scale(.5)}80%{opacity:.8;transform:scale(1.1)}100%{opacity:1;transform:scale(1)}}.layui-anim-scaleSpring{-webkit-animation-name:layui-scale-spring;animation-name:layui-scale-spring}@-webkit-keyframes layui-fadein{0%{opacity:0}100%{opacity:1}}@keyframes layui-fadein{0%{opacity:0}100%{opacity:1}}.layui-anim-fadein{-webkit-animation-name:layui-fadein;animation-name:layui-fadein}@-webkit-keyframes layui-fadeout{0%{opacity:1}100%{opacity:0}}@keyframes layui-fadeout{0%{opacity:1}100%{opacity:0}}.layui-anim-fadeout{-webkit-animation-name:layui-fadeout;animation-name:layui-fadeout}

================================================
FILE: public/layui/css/layui.mobile.css
================================================
/** layui-v2.3.0 MIT License By https://www.layui.com */
 blockquote,body,button,dd,div,dl,dt,form,h1,h2,h3,h4,h5,h6,input,legend,li,ol,p,td,textarea,th,ul{margin:0;padding:0;-webkit-tap-highlight-color:rgba(0,0,0,0)}html{font:12px 'Helvetica Neue','PingFang SC',STHeitiSC-Light,Helvetica,Arial,sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}a,button,input{-webkit-tap-highlight-color:rgba(255,0,0,0)}a{text-decoration:none;background:0 0}a:active,a:hover{outline:0}table{border-collapse:collapse;border-spacing:0}li{list-style:none}b,strong{font-weight:700}h1,h2,h3,h4,h5,h6{font-weight:500}address,cite,dfn,em,var{font-style:normal}dfn{font-style:italic}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}img{border:0;vertical-align:bottom}.layui-inline,input,label{vertical-align:middle}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0;outline:0}button,select{text-transform:none}select{-webkit-appearance:none;border:none}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}@font-face{font-family:layui-icon;src:url(../font/iconfont.eot?v=1.0.7);src:url(../font/iconfont.eot?v=1.0.7#iefix) format('embedded-opentype'),url(../font/iconfont.woff?v=1.0.7) format('woff'),url(../font/iconfont.ttf?v=1.0.7) format('truetype'),url(../font/iconfont.svg?v=1.0.7#iconfont) format('svg')}.layui-icon{font-family:layui-icon!important;font-size:16px;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.layui-box,.layui-box *{-webkit-box-sizing:content-box!important;-moz-box-sizing:content-box!important;box-sizing:content-box!important}.layui-border-box,.layui-border-box *{-webkit-box-sizing:border-box!important;-moz-box-sizing:border-box!important;box-sizing:border-box!important}.layui-inline{position:relative;display:inline-block;*display:inline;*zoom:1}.layui-edge,.layui-upload-iframe{position:absolute;width:0;height:0}.layui-edge{border-style:dashed;border-color:transparent;overflow:hidden}.layui-elip{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-unselect{-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.layui-disabled,.layui-disabled:active{background-color:#d2d2d2!important;color:#fff!important;cursor:not-allowed!important}.layui-circle{border-radius:100%}.layui-show{display:block!important}.layui-hide{display:none!important}.layui-upload-iframe{border:0;visibility:hidden}.layui-upload-enter{border:1px solid #009E94;background-color:#009E94;color:#fff;-webkit-transform:scale(1.1);transform:scale(1.1)}@-webkit-keyframes layui-m-anim-scale{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes layui-m-anim-scale{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}.layui-m-anim-scale{animation-name:layui-m-anim-scale;-webkit-animation-name:layui-m-anim-scale}@-webkit-keyframes layui-m-anim-up{0%{opacity:0;-webkit-transform:translateY(800px);transform:translateY(800px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes layui-m-anim-up{0%{opacity:0;-webkit-transform:translateY(800px);transform:translateY(800px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}.layui-m-anim-up{-webkit-animation-name:layui-m-anim-up;animation-name:layui-m-anim-up}@-webkit-keyframes layui-m-anim-left{0%{-webkit-transform:translateX(100%);transform:translateX(100%)}100%{-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes layui-m-anim-left{0%{-webkit-transform:translateX(100%);transform:translateX(100%)}100%{-webkit-transform:translateX(0);transform:translateX(0)}}.layui-m-anim-left{-webkit-animation-name:layui-m-anim-left;animation-name:layui-m-anim-left}@-webkit-keyframes layui-m-anim-right{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%)}100%{-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes layui-m-anim-right{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%)}100%{-webkit-transform:translateX(0);transform:translateX(0)}}.layui-m-anim-right{-webkit-animation-name:layui-m-anim-right;animation-name:layui-m-anim-right}@-webkit-keyframes layui-m-anim-lout{0%{-webkit-transform:translateX(0);transform:translateX(0)}100%{-webkit-transform:translateX(-100%);transform:translateX(-100%)}}@keyframes layui-m-anim-lout{0%{-webkit-transform:translateX(0);transform:translateX(0)}100%{-webkit-transform:translateX(-100%);transform:translateX(-100%)}}.layui-m-anim-lout{-webkit-animation-name:layui-m-anim-lout;animation-name:layui-m-anim-lout}@-webkit-keyframes layui-m-anim-rout{0%{-webkit-transform:translateX(0);transform:translateX(0)}100%{-webkit-transform:translateX(100%);transform:translateX(100%)}}@keyframes layui-m-anim-rout{0%{-webkit-transform:translateX(0);transform:translateX(0)}100%{-webkit-transform:translateX(100%);transform:translateX(100%)}}.layui-m-anim-rout{-webkit-animation-name:layui-m-anim-rout;animation-name:layui-m-anim-rout}.layui-m-layer{position:relative;z-index:19891014}.layui-m-layer *{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.layui-m-layermain,.layui-m-layershade{position:fixed;left:0;top:0;width:100%;height:100%}.layui-m-layershade{background-color:rgba(0,0,0,.7);pointer-events:auto}.layui-m-layermain{display:table;font-family:Helvetica,arial,sans-serif;pointer-events:none}.layui-m-layermain .layui-m-layersection{display:table-cell;vertical-align:middle;text-align:center}.layui-m-layerchild{position:relative;display:inline-block;text-align:left;background-color:#fff;font-size:14px;border-radius:5px;box-shadow:0 0 8px rgba(0,0,0,.1);pointer-events:auto;-webkit-overflow-scrolling:touch;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.2s;animation-duration:.2s}.layui-m-layer0 .layui-m-layerchild{width:90%;max-width:640px}.layui-m-layer1 .layui-m-layerchild{border:none;border-radius:0}.layui-m-layer2 .layui-m-layerchild{width:auto;max-width:260px;min-width:40px;border:none;background:0 0;box-shadow:none;color:#fff}.layui-m-layerchild h3{padding:0 10px;height:60px;line-height:60px;font-size:16px;font-weight:400;border-radius:5px 5px 0 0;text-align:center}.layui-m-layerbtn span,.layui-m-layerchild h3{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-m-layercont{padding:50px 30px;line-height:22px;text-align:center}.layui-m-layer1 .layui-m-layercont{padding:0;text-align:left}.layui-m-layer2 .layui-m-layercont{text-align:center;padding:0;line-height:0}.layui-m-layer2 .layui-m-layercont i{width:25px;height:25px;margin-left:8px;display:inline-block;background-color:#fff;border-radius:100%;-webkit-animation:layui-m-anim-loading 1.4s infinite ease-in-out;animation:layui-m-anim-loading 1.4s infinite ease-in-out;-webkit-animation-fill-mode:both;animation-fill-mode:both}.layui-m-layerbtn,.layui-m-layerbtn span{position:relative;text-align:center;border-radius:0 0 5px 5px}.layui-m-layer2 .layui-m-layercont p{margin-top:20px}@-webkit-keyframes layui-m-anim-loading{0%,100%,80%{transform:scale(0);-webkit-transform:scale(0)}40%{transform:scale(1);-webkit-transform:scale(1)}}@keyframes layui-m-anim-loading{0%,100%,80%{transform:scale(0);-webkit-transform:scale(0)}40%{transform:scale(1);-webkit-transform:scale(1)}}.layui-m-layer2 .layui-m-layercont i:first-child{margin-left:0;-webkit-animation-delay:-.32s;animation-delay:-.32s}.layui-m-layer2 .layui-m-layercont i.layui-m-layerload{-webkit-animation-delay:-.16s;animation-delay:-.16s}.layui-m-layer2 .layui-m-layercont>div{line-height:22px;padding-top:7px;margin-bottom:20px;font-size:14px}.layui-m-layerbtn{display:box;display:-moz-box;display:-webkit-box;width:100%;height:50px;line-height:50px;font-size:0;border-top:1px solid #D0D0D0;background-color:#F2F2F2}.layui-m-layerbtn span{display:block;-moz-box-flex:1;box-flex:1;-webkit-box-flex:1;font-size:14px;cursor:pointer}.layui-m-layerbtn span[yes]{color:#40AFFE}.layui-m-layerbtn span[no]{border-right:1px solid #D0D0D0;border-radius:0 0 0 5px}.layui-m-layerbtn span:active{background-color:#F6F6F6}.layui-m-layerend{position:absolute;right:7px;top:10px;width:30px;height:30px;border:0;font-weight:400;background:0 0;cursor:pointer;-webkit-appearance:none;font-size:30px}.layui-m-layerend::after,.layui-m-layerend::before{position:absolute;left:5px;top:15px;content:'';width:18px;height:1px;background-color:#999;transform:rotate(45deg);-webkit-transform:rotate(45deg);border-radius:3px}.layui-m-layerend::after{transform:rotate(-45deg);-webkit-transform:rotate(-45deg)}body .layui-m-layer .layui-m-layer-footer{position:fixed;width:95%;max-width:100%;margin:0 auto;left:0;right:0;bottom:10px;background:0 0}.layui-m-layer-footer .layui-m-layercont{padding:20px;border-radius:5px 5px 0 0;background-color:rgba(255,255,255,.8)}.layui-m-layer-footer .layui-m-layerbtn{display:block;height:auto;background:0 0;border-top:none}.layui-m-layer-footer .layui-m-layerbtn span{background-color:rgba(255,255,255,.8)}.layui-m-layer-footer .layui-m-layerbtn span[no]{color:#FD482C;border-top:1px solid #c2c2c2;border-radius:0 0 5px 5px}.layui-m-layer-footer .layui-m-layerbtn span[yes]{margin-top:10px;border-radius:5px}body .layui-m-layer .layui-m-layer-msg{width:auto;max-width:90%;margin:0 auto;bottom:-150px;background-color:rgba(0,0,0,.7);color:#fff}.layui-m-layer-msg .layui-m-layercont{padding:10px 20px}

================================================
FILE: public/layui/css/modules/code.css
================================================
/** layui-v2.3.0 MIT License By https://www.layui.com */
 html #layuicss-skincodecss{display:none;position:absolute;width:1989px}.layui-code-h3,.layui-code-view{position:relative;font-size:12px}.layui-code-view{display:block;margin:10px 0;padding:0;border:1px solid #e2e2e2;border-left-width:6px;background-color:#F2F2F2;color:#333;font-family:Courier New}.layui-code-h3{padding:0 10px;height:32px;line-height:32px;border-bottom:1px solid #e2e2e2}.layui-code-h3 a{position:absolute;right:10px;top:0;color:#999}.layui-code-view .layui-code-ol{position:relative;overflow:auto}.layui-code-view .layui-code-ol li{position:relative;margin-left:45px;line-height:20px;padding:0 5px;border-left:1px solid #e2e2e2;list-style-type:decimal-leading-zero;*list-style-type:decimal;background-color:#fff}.layui-code-view pre{margin:0}.layui-code-notepad{border:1px solid #0C0C0C;border-left-color:#3F3F3F;background-color:#0C0C0C;color:#C2BE9E}.layui-code-notepad .layui-code-h3{border-bottom:none}.layui-code-notepad .layui-code-ol li{background-color:#3F3F3F;border-left:none}

================================================
FILE: public/layui/css/modules/laydate/default/laydate.css
================================================
/** layui-v2.3.0 MIT License By https://www.layui.com */
 .laydate-set-ym,.layui-laydate,.layui-laydate *,.layui-laydate-list{box-sizing:border-box}html #layuicss-laydate{display:none;position:absolute;width:1989px}.layui-laydate *{margin:0;padding:0}.layui-laydate{position:absolute;z-index:66666666;margin:5px 0;border-radius:2px;font-size:14px;-webkit-animation-duration:.3s;animation-duration:.3s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-name:laydate-upbit;animation-name:laydate-upbit}.layui-laydate-main{width:272px}.layui-laydate-content td,.layui-laydate-header *,.layui-laydate-list li{transition-duration:.3s;-webkit-transition-duration:.3s}@-webkit-keyframes laydate-upbit{from{-webkit-transform:translate3d(0,20px,0);opacity:.3}to{-webkit-transform:translate3d(0,0,0);opacity:1}}@keyframes laydate-upbit{from{transform:translate3d(0,20px,0);opacity:.3}to{transform:translate3d(0,0,0);opacity:1}}.layui-laydate-static{position:relative;z-index:0;display:inline-block;margin:0;-webkit-animation:none;animation:none}.laydate-ym-show .laydate-next-m,.laydate-ym-show .laydate-prev-m{display:none!important}.laydate-ym-show .laydate-next-y,.laydate-ym-show .laydate-prev-y{display:inline-block!important}.laydate-time-show .laydate-set-ym span[lay-type=month],.laydate-time-show .laydate-set-ym span[lay-type=year],.laydate-time-show .layui-laydate-header .layui-icon,.laydate-ym-show .laydate-set-ym span[lay-type=month]{display:none!important}.layui-laydate-header{position:relative;line-height:30px;padding:10px 70px 5px}.laydate-set-ym span,.layui-laydate-header i{padding:0 5px;cursor:pointer}.layui-laydate-header *{display:inline-block;vertical-align:bottom}.layui-laydate-header i{position:absolute;top:10px;color:#999;font-size:18px}.layui-laydate-header i.laydate-prev-y{left:15px}.layui-laydate-header i.laydate-prev-m{left:45px}.layui-laydate-header i.laydate-next-y{right:15px}.layui-laydate-header i.laydate-next-m{right:45px}.laydate-set-ym{width:100%;text-align:center;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.laydate-time-text{cursor:default!important}.layui-laydate-content{position:relative;padding:10px;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.layui-laydate-content table{border-collapse:collapse;border-spacing:0}.layui-laydate-content td,.layui-laydate-content th{width:36px;height:30px;padding:5px;text-align:center}.layui-laydate-content td{position:relative;cursor:pointer}.laydate-day-mark{position:absolute;left:0;top:0;width:100%;height:100%;line-height:30px;font-size:12px;overflow:hidden}.laydate-day-mark::after{position:absolute;content:'';right:2px;top:2px;width:5px;height:5px;border-radius:50%}.layui-laydate-footer{position:relative;height:46px;line-height:26px;padding:10px 20px}.layui-laydate-footer span{margin-right:15px;display:inline-block;cursor:pointer;font-size:12px}.layui-laydate-footer span:hover{color:#5FB878}.laydate-footer-btns{position:absolute;right:10px;top:10px}.laydate-footer-btns span{height:26px;line-height:26px;margin:0 0 0 -1px;padding:0 10px;border:1px solid #C9C9C9;background-color:#fff;white-space:nowrap;vertical-align:top;border-radius:2px}.layui-laydate-list>li,.layui-laydate-range .layui-laydate-main{display:inline-block;vertical-align:middle}.layui-laydate-list{position:absolute;left:0;top:0;width:100%;height:100%;padding:10px;background-color:#fff}.layui-laydate-list>li{position:relative;width:33.3%;height:36px;line-height:36px;margin:3px 0;text-align:center;cursor:pointer}.laydate-month-list>li{width:25%;margin:17px 0}.laydate-time-list>li{height:100%;margin:0;line-height:normal;cursor:default}.laydate-time-list p{position:relative;top:-4px;line-height:29px}.laydate-time-list ol{height:181px;overflow:hidden}.laydate-time-list>li:hover ol{overflow-y:auto}.laydate-time-list ol li{width:130%;padding-left:33px;line-height:30px;text-align:left;cursor:pointer}.layui-laydate-hint{position:absolute;top:115px;left:50%;width:250px;margin-left:-125px;line-height:20px;padding:15px;text-align:center;font-size:12px}.layui-laydate-range{width:546px}.layui-laydate-range .laydate-main-list-0 .laydate-next-m,.layui-laydate-range .laydate-main-list-0 .laydate-next-y,.layui-laydate-range .laydate-main-list-1 .laydate-prev-m,.layui-laydate-range .laydate-main-list-1 .laydate-prev-y{display:none}.layui-laydate-range .laydate-main-list-1 .layui-laydate-content{border-left:1px solid #e2e2e2}.layui-laydate,.layui-laydate-hint{border:1px solid #d2d2d2;box-shadow:0 2px 4px rgba(0,0,0,.12);background-color:#fff;color:#666}.layui-laydate-header{border-bottom:1px solid #e2e2e2}.layui-laydate-header i:hover,.layui-laydate-header span:hover{color:#5FB878}.layui-laydate-content{border-top:none 0;border-bottom:none 0}.layui-laydate-content th{font-weight:400;color:#333}.layui-laydate-content td{color:#666}.layui-laydate-content td.laydate-selected{background-color:#00F7DE}.laydate-selected:hover{background-color:#00F7DE!important}.layui-laydate-content td:hover,.layui-laydate-list li:hover{background-color:#eaeaea;color:#333}.laydate-time-list li ol{margin:0;padding:0;border:1px solid #e2e2e2;border-left-width:0}.laydate-time-list li:first-child ol{border-left-width:1px}.laydate-time-list>li:hover{background:0 0}.layui-laydate-content .laydate-day-next,.layui-laydate-content .laydate-day-prev{color:#d2d2d2}.laydate-selected.laydate-day-next,.laydate-selected.laydate-day-prev{background-color:#f8f8f8!important}.layui-laydate-footer{border-top:1px solid #e2e2e2}.layui-laydate-hint{color:#FF5722}.laydate-day-mark::after{background-color:#5FB878}.layui-laydate-content td.layui-this .laydate-day-mark::after{display:none}.layui-laydate-footer span[lay-type=date]{color:#5FB878}.layui-laydate .layui-this{background-color:#009688!important;color:#fff!important}.layui-laydate .laydate-disabled,.layui-laydate .laydate-disabled:hover{background:0 0!important;color:#d2d2d2!important;cursor:not-allowed!important;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.laydate-theme-molv{border:none}.laydate-theme-molv.layui-laydate-range{width:548px}.laydate-theme-molv .layui-laydate-main{width:274px}.laydate-theme-molv .layui-laydate-header{border:none;background-color:#009688}.laydate-theme-molv .layui-laydate-header i,.laydate-theme-molv .layui-laydate-header span{color:#f6f6f6}.laydate-theme-molv .layui-laydate-header i:hover,.laydate-theme-molv .layui-laydate-header span:hover{color:#fff}.laydate-theme-molv .layui-laydate-content{border:1px solid #e2e2e2;border-top:none;border-bottom:none}.laydate-theme-molv .laydate-main-list-1 .layui-laydate-content{border-left:none}.laydate-theme-grid .laydate-month-list>li,.laydate-theme-grid .laydate-year-list>li,.laydate-theme-grid .layui-laydate-content td,.laydate-theme-grid .layui-laydate-content thead,.laydate-theme-molv .layui-laydate-footer{border:1px solid #e2e2e2}.laydate-theme-grid .laydate-selected,.laydate-theme-grid .laydate-selected:hover{background-color:#f2f2f2!important;color:#009688!important}.laydate-theme-grid .laydate-selected.laydate-day-next,.laydate-theme-grid .laydate-selected.laydate-day-prev{color:#d2d2d2!important}.laydate-theme-grid .laydate-month-list,.laydate-theme-grid .laydate-year-list{margin:1px 0 0 1px}.laydate-theme-grid .laydate-month-list>li,.laydate-theme-grid .laydate-year-list>li{margin:0 -1px -1px 0}.laydate-theme-grid .laydate-year-list>li{height:43px;line-height:43px}.laydate-theme-grid .laydate-month-list>li{height:71px;line-height:71px}

================================================
FILE: public/layui/css/modules/layer/default/layer.css
================================================
/** layui-v2.3.0 MIT License By https://www.layui.com */
 .layui-layer-imgbar,.layui-layer-imgtit a,.layui-layer-tab .layui-layer-title span,.layui-layer-title{text-overflow:ellipsis;white-space:nowrap}html #layuicss-layer{display:none;position:absolute;width:1989px}.layui-layer,.layui-layer-shade{position:fixed;_position:absolute;pointer-events:auto}.layui-layer-shade{top:0;left:0;width:100%;height:100%;_height:expression(document.body.offsetHeight+"px")}.layui-layer{-webkit-overflow-scrolling:touch;top:150px;left:0;margin:0;padding:0;background-color:#fff;-webkit-background-clip:content;border-radius:2px;box-shadow:1px 1px 50px rgba(0,0,0,.3)}.layui-layer-close{position:absolute}.layui-layer-content{position:relative}.layui-layer-border{border:1px solid #B2B2B2;border:1px solid rgba(0,0,0,.1);box-shadow:1px 1px 5px rgba(0,0,0,.2)}.layui-layer-load{background:url(loading-1.gif) center center no-repeat #eee}.layui-layer-ico{background:url(icon.png) no-repeat}.layui-layer-btn a,.layui-layer-dialog .layui-layer-ico,.layui-layer-setwin a{display:inline-block;*display:inline;*zoom:1;vertical-align:top}.layui-layer-move{display:none;position:fixed;*position:absolute;left:0;top:0;width:100%;height:100%;cursor:move;opacity:0;filter:alpha(opacity=0);background-color:#fff;z-index:2147483647}.layui-layer-resize{position:absolute;width:15px;height:15px;right:0;bottom:0;cursor:se-resize}.layer-anim{-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.3s;animation-duration:.3s}@-webkit-keyframes layer-bounceIn{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes layer-bounceIn{0%{opacity:0;-webkit-transform:scale(.5);-ms-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layer-anim-00{-webkit-animation-name:layer-bounceIn;animation-name:layer-bounceIn}@-webkit-keyframes layer-zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes layer-zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);-ms-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);-ms-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layer-anim-01{-webkit-animation-name:layer-zoomInDown;animation-name:layer-zoomInDown}@-webkit-keyframes layer-fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes layer-fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);-ms-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}.layer-anim-02{-webkit-animation-name:layer-fadeInUpBig;animation-name:layer-fadeInUpBig}@-webkit-keyframes layer-zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes layer-zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);-ms-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);-ms-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layer-anim-03{-webkit-animation-name:layer-zoomInLeft;animation-name:layer-zoomInLeft}@-webkit-keyframes layer-rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}@keyframes layer-rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);-ms-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);-ms-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}.layer-anim-04{-webkit-animation-name:layer-rollIn;animation-name:layer-rollIn}@keyframes layer-fadeIn{0%{opacity:0}100%{opacity:1}}.layer-anim-05{-webkit-animation-name:layer-fadeIn;animation-name:layer-fadeIn}@-webkit-keyframes layer-shake{0%,100%{-webkit-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);transform:translateX(10px)}}@keyframes layer-shake{0%,100%{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);-ms-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);-ms-transform:translateX(10px);transform:translateX(10px)}}.layer-anim-06{-webkit-animation-name:layer-shake;animation-name:layer-shake}@-webkit-keyframes fadeIn{0%{opacity:0}100%{opacity:1}}.layui-layer-title{padding:0 80px 0 20px;height:42px;line-height:42px;border-bottom:1px solid #eee;font-size:14px;color:#333;overflow:hidden;background-color:#F8F8F8;border-radius:2px 2px 0 0}.layui-layer-setwin{position:absolute;right:15px;*right:0;top:15px;font-size:0;line-height:initial}.layui-layer-setwin a{position:relative;width:16px;height:16px;margin-left:10px;font-size:12px;_overflow:hidden}.layui-layer-setwin .layui-layer-min cite{position:absolute;width:14px;height:2px;left:0;top:50%;margin-top:-1px;background-color:#2E2D3C;cursor:pointer;_overflow:hidden}.layui-layer-setwin .layui-layer-min:hover cite{background-color:#2D93CA}.layui-layer-setwin .layui-layer-max{background-position:-32px -40px}.layui-layer-setwin .layui-layer-max:hover{background-position:-16px -40px}.layui-layer-setwin .layui-layer-maxmin{background-position:-65px -40px}.layui-layer-setwin .layui-layer-maxmin:hover{background-position:-49px -40px}.layui-layer-setwin .layui-layer-close1{background-position:1px -40px;cursor:pointer}.layui-layer-setwin .layui-layer-close1:hover{opacity:.7}.layui-layer-setwin .layui-layer-close2{position:absolute;right:-28px;top:-28px;width:30px;height:30px;margin-left:0;background-position:-149px -31px;*right:-18px;_display:none}.layui-layer-setwin .layui-layer-close2:hover{background-position:-180px -31px}.layui-layer-btn{text-align:right;padding:0 15px 12px;pointer-events:auto;user-select:none;-webkit-user-select:none}.layui-layer-btn a{height:28px;line-height:28px;margin:5px 5px 0;padding:0 15px;border:1px solid #dedede;background-color:#fff;color:#333;border-radius:2px;font-weight:400;cursor:pointer;text-decoration:none}.layui-layer-btn a:hover{opacity:.9;text-decoration:none}.layui-layer-btn a:active{opacity:.8}.layui-layer-btn .layui-layer-btn0{border-color:#1E9FFF;background-color:#1E9FFF;color:#fff}.layui-layer-btn-l{text-align:left}.layui-layer-btn-c{text-align:center}.layui-layer-dialog{min-width:260px}.layui-layer-dialog .layui-layer-content{position:relative;padding:20px;line-height:24px;word-break:break-all;overflow:hidden;font-size:14px;overflow-x:hidden;overflow-y:auto}.layui-layer-dialog .layui-layer-content .layui-layer-ico{position:absolute;top:16px;left:15px;_left:-40px;width:30px;height:30px}.layui-layer-ico1{background-position:-30px 0}.layui-layer-ico2{background-position:-60px 0}.layui-layer-ico3{background-position:-90px 0}.layui-layer-ico4{background-position:-120px 0}.layui-layer-ico5{background-position:-150px 0}.layui-layer-ico6{background-position:-180px 0}.layui-layer-rim{border:6px solid #8D8D8D;border:6px solid rgba(0,0,0,.3);border-radius:5px;box-shadow:none}.layui-layer-msg{min-width:180px;border:1px solid #D3D4D3;box-shadow:none}.layui-layer-hui{min-width:100px;background-color:#000;filter:alpha(opacity=60);background-color:rgba(0,0,0,.6);color:#fff;border:none}.layui-layer-hui .layui-layer-content{padding:12px 25px;text-align:center}.layui-layer-dialog .layui-layer-padding{padding:20px 20px 20px 55px;text-align:left}.layui-layer-page .layui-layer-content{position:relative;overflow:auto}.layui-layer-iframe .layui-layer-btn,.layui-layer-page .layui-layer-btn{padding-top:10px}.layui-layer-nobg{background:0 0}.layui-layer-iframe iframe{display:block;width:100%}.layui-layer-loading{border-radius:100%;background:0 0;box-shadow:none;border:none}.layui-layer-loading .layui-layer-content{width:60px;height:24px;background:url(loading-0.gif) no-repeat}.layui-layer-loading .layui-layer-loading1{width:37px;height:37px;background:url(loading-1.gif) no-repeat}.layui-layer-ico16,.layui-layer-loading .layui-layer-loading2{width:32px;height:32px;background:url(loading-2.gif) no-repeat}.layui-layer-tips{background:0 0;box-shadow:none;border:none}.layui-layer-tips .layui-layer-content{position:relative;line-height:22px;min-width:12px;padding:8px 15px;font-size:12px;_float:left;border-radius:2px;box-shadow:1px 1px 3px rgba(0,0,0,.2);background-color:#000;color:#fff}.layui-layer-tips .layui-layer-close{right:-2px;top:-1px}.layui-layer-tips i.layui-layer-TipsG{position:absolute;width:0;height:0;border-width:8px;border-color:transparent;border-style:dashed;*overflow:hidden}.layui-layer-tips i.layui-layer-TipsB,.layui-layer-tips i.layui-layer-TipsT{left:5px;border-right-style:solid;border-right-color:#000}.layui-layer-tips i.layui-layer-TipsT{bottom:-8px}.layui-layer-tips i.layui-layer-TipsB{top:-8px}.layui-layer-tips i.layui-layer-TipsL,.layui-layer-tips i.layui-layer-TipsR{top:5px;border-bottom-style:solid;border-bottom-color:#000}.layui-layer-tips i.layui-layer-TipsR{left:-8px}.layui-layer-tips i.layui-layer-TipsL{right:-8px}.layui-layer-lan[type=dialog]{min-width:280px}.layui-layer-lan .layui-layer-title{background:#4476A7;color:#fff;border:none}.layui-layer-lan .layui-layer-btn{padding:5px 10px 10px;text-align:right;border-top:1px solid #E9E7E7}.layui-layer-lan .layui-layer-btn a{background:#fff;border-color:#E9E7E7;color:#333}.layui-layer-lan .layui-layer-btn .layui-layer-btn1{background:#C9C5C5}.layui-layer-molv .layui-layer-title{background:#009f95;color:#fff;border:none}.layui-layer-molv .layui-layer-btn a{background:#009f95;border-color:#009f95}.layui-layer-molv .layui-layer-btn .layui-layer-btn1{background:#92B8B1}.layui-layer-iconext{background:url(icon-ext.png) no-repeat}.layui-layer-prompt .layui-layer-input{display:block;width:230px;height:36px;margin:0 auto;line-height:30px;padding-left:10px;border:1px solid #e6e6e6;color:#333}.layui-layer-prompt textarea.layui-layer-input{width:300px;height:100px;line-height:20px;padding:6px 10px}.layui-layer-prompt .layui-layer-content{padding:20px}.layui-layer-prompt .layui-layer-btn{padding-top:0}.layui-layer-tab{box-shadow:1px 1px 50px rgba(0,0,0,.4)}.layui-layer-tab .layui-layer-title{padding-left:0;overflow:visible}.layui-layer-tab .layui-layer-title span{position:relative;float:left;min-width:80px;max-width:260px;padding:0 20px;text-align:center;overflow:hidden;cursor:pointer}.layui-layer-tab .layui-layer-title span.layui-this{height:43px;border-left:1px solid #eee;border-right:1px solid #eee;background-color:#fff;z-index:10}.layui-layer-tab .layui-layer-title span:first-child{border-left:none}.layui-layer-tabmain{line-height:24px;clear:both}.layui-layer-tabmain .layui-layer-tabli{display:none}.layui-layer-tabmain .layui-layer-tabli.layui-this{display:block}.layui-layer-photos{-webkit-animation-duration:.8s;animation-duration:.8s}.layui-layer-photos .layui-layer-content{overflow:hidden;text-align:center}.layui-layer-photos .layui-layer-phimg img{position:relative;width:100%;display:inline-block;*display:inline;*zoom:1;vertical-align:top}.layui-layer-imgbar,.layui-layer-imguide{display:none}.layui-layer-imgnext,.layui-layer-imgprev{position:absolute;top:50%;width:27px;_width:44px;height:44px;margin-top:-22px;outline:0;blr:expression(this.onFocus=this.blur())}.layui-layer-imgprev{left:10px;background-position:-5px -5px;_background-position:-70px -5px}.layui-layer-imgprev:hover{background-position:-33px -5px;_background-position:-120px -5px}.layui-layer-imgnext{right:10px;_right:8px;background-position:-5px -50px;_background-position:-70px -50px}.layui-layer-imgnext:hover{background-position:-33px -50px;_background-position:-120px -50px}.layui-layer-imgbar{position:absolute;left:0;bottom:0;width:100%;height:32px;line-height:32px;background-color:rgba(0,0,0,.8);background-color:#000\9;filter:Alpha(opacity=80);color:#fff;overflow:hidden;font-size:0}.layui-layer-imgtit *{display:inline-block;*display:inline;*zoom:1;vertical-align:top;font-size:12px}.layui-layer-imgtit a{max-width:65%;overflow:hidden;color:#fff}.layui-layer-imgtit a:hover{color:#fff;text-decoration:underline}.layui-layer-imgtit em{padding-left:10px;font-style:normal}@-webkit-keyframes layer-bounceOut{100%{opacity:0;-webkit-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.05);transform:scale(1.05)}0%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes layer-bounceOut{100%{opacity:0;-webkit-transform:scale(.7);-ms-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.05);-ms-transform:scale(1.05);transform:scale(1.05)}0%{-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layer-anim-close{-webkit-animation-name:layer-bounceOut;animation-name:layer-bounceOut;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.2s;animation-duration:.2s}@media screen and (max-width:1100px){.layui-layer-iframe{overflow-y:auto;-webkit-overflow-scrolling:touch}}

================================================
FILE: public/layui/lay/modules/carousel.js
================================================
/** layui-v2.3.0 MIT License By https://www.layui.com */
 ;layui.define("jquery",function(e){"use strict";var i=layui.$,n=(layui.hint(),layui.device(),{config:{},set:function(e){var n=this;return n.config=i.extend({},n.config,e),n},on:function(e,i){return layui.onevent.call(this,t,e,i)}}),t="carousel",a="layui-this",l=">*[carousel-item]>*",o="layui-carousel-left",r="layui-carousel-right",d="layui-carousel-prev",s="layui-carousel-next",u="layui-carousel-arrow",c="layui-carousel-ind",m=function(e){var t=this;t.config=i.extend({},t.config,n.config,e),t.render()};m.prototype.config={width:"600px",height:"280px",full:!1,arrow:"hover",indicator:"inside",autoplay:!0,interval:3e3,anim:"",trigger:"click",index:0},m.prototype.render=function(){var e=this,n=e.config;n.elem=i(n.elem),n.elem[0]&&(e.elemItem=n.elem.find(l),n.index<0&&(n.index=0),n.index>=e.elemItem.length&&(n.index=e.elemItem.length-1),n.interval<800&&(n.interval=800),n.full?n.elem.css({position:"fixed",width:"100%",height:"100%",zIndex:9999}):n.elem.css({width:n.width,height:n.height}),n.elem.attr("lay-anim",n.anim),e.elemItem.eq(n.index).addClass(a),e.elemItem.length<=1||(e.indicator(),e.arrow(),e.autoplay(),e.events()))},m.prototype.reload=function(e){var n=this;clearInterval(n.timer),n.config=i.extend({},n.config,e),n.render()},m.prototype.prevIndex=function(){var e=this,i=e.config,n=i.index-1;return n<0&&(n=e.elemItem.length-1),n},m.prototype.nextIndex=function(){var e=this,i=e.config,n=i.index+1;return n>=e.elemItem.length&&(n=0),n},m.prototype.addIndex=function(e){var i=this,n=i.config;e=e||1,n.index=n.index+e,n.index>=i.elemItem.length&&(n.index=0)},m.prototype.subIndex=function(e){var i=this,n=i.config;e=e||1,n.index=n.index-e,n.index<0&&(n.index=i.elemItem.length-1)},m.prototype.autoplay=function(){var e=this,i=e.config;i.autoplay&&(e.timer=setInterval(function(){e.slide()},i.interval))},m.prototype.arrow=function(){var e=this,n=e.config,t=i(['<button class="layui-icon '+u+'" lay-type="sub">'+("updown"===n.anim?"&#xe619;":"&#xe603;")+"</button>",'<button class="layui-icon '+u+'" lay-type="add">'+("updown"===n.anim?"&#xe61a;":"&#xe602;")+"</button>"].join(""));n.elem.attr("lay-arrow",n.arrow),n.elem.find("."+u)[0]&&n.elem.find("."+u).remove(),n.elem.append(t),t.on("click",function(){var n=i(this),t=n.attr("lay-type");e.slide(t)})},m.prototype.indicator=function(){var e=this,n=e.config,t=e.elemInd=i(['<div class="'+c+'"><ul>',function(){var i=[];return layui.each(e.elemItem,function(e){i.push("<li"+(n.index===e?' class="layui-this"':"")+"></li>")}),i.join("")}(),"</ul></div>"].join(""));n.elem.attr("lay-indicator",n.indicator),n.elem.find("."+c)[0]&&n.elem.find("."+c).remove(),n.elem.append(t),"updown"===n.anim&&t.css("margin-top",-(t.height()/2)),t.find("li").on("hover"===n.trigger?"mouseover":n.trigger,function(){var t=i(this),a=t.index();a>n.index?e.slide("add",a-n.index):a<n.index&&e.slide("sub",n.index-a)})},m.prototype.slide=function(e,i){var n=this,l=n.elemItem,u=n.config,c=u.index,m=u.elem.attr("lay-filter");n.haveSlide||("sub"===e?(n.subIndex(i),l.eq(u.index).addClass(d),setTimeout(function(){l.eq(c).addClass(r),l.eq(u.index).addClass(r)},50)):(n.addIndex(i),l.eq(u.index).addClass(s),setTimeout(function(){l.eq(c).addClass(o),l.eq(u.index).addClass(o)},50)),setTimeout(function(){l.removeClass(a+" "+d+" "+s+" "+o+" "+r),l.eq(u.index).addClass(a),n.haveSlide=!1},300),n.elemInd.find("li").eq(u.index).addClass(a).siblings().removeClass(a),n.haveSlide=!0,layui.event.call(this,t,"change("+m+")",{index:u.index,prevIndex:c,item:l.eq(u.index)}))},m.prototype.events=function(){var e=this,i=e.config;i.elem.data("haveEvents")||(i.elem.on("mouseenter",function(){clearInterval(e.timer)}).on("mouseleave",function(){e.autoplay()}),i.elem.data("haveEvents",!0))},n.render=function(e){var i=new m(e);return i},e(t,n)});

================================================
FILE: public/layui/lay/modules/code.js
================================================
/** layui-v2.3.0 MIT License By https://www.layui.com */
 ;layui.define("jquery",function(e){"use strict";var a=layui.$,l="http://www.layui.com/doc/modules/code.html";e("code",function(e){var t=[];e=e||{},e.elem=a(e.elem||".layui-code"),e.about=!("about"in e)||e.about,e.elem.each(function(){t.push(this)}),layui.each(t.reverse(),function(t,i){var c=a(i),o=c.html();(c.attr("lay-encode")||e.encode)&&(o=o.replace(/&(?!#?[a-zA-Z0-9]+;)/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/'/g,"&#39;").replace(/"/g,"&quot;")),c.html('<ol class="layui-code-ol"><li>'+o.replace(/[\r\t\n]+/g,"</li><li>")+"</li></ol>"),c.find(">.layui-code-h3")[0]||c.prepend('<h3 class="layui-code-h3">'+(c.attr("lay-title")||e.title||"code")+(e.about?'<a href="'+l+'" target="_blank">layui.code</a>':"")+"</h3>");var d=c.find(">.layui-code-ol");c.addClass("layui-box layui-code-view"),(c.attr("lay-skin")||e.skin)&&c.addClass("layui-code-"+(c.attr("lay-skin")||e.skin)),(d.find("li").length/100|0)>0&&d.css("margin-left",(d.find("li").length/100|0)+"px"),(c.attr("lay-height")||e.height)&&d.css("max-height",c.attr("lay-height")||e.height)})})}).addcss("modules/code.css","skincodecss");

================================================
FILE: public/layui/lay/modules/element.js
================================================
/** layui-v2.3.0 MIT License By https://www.layui.com */
 ;layui.define("jquery",function(t){"use strict";var a=layui.$,i=(layui.hint(),layui.device()),e="element",l="layui-this",n="layui-show",s=function(){this.config={}};s.prototype.set=function(t){var i=this;return a.extend(!0,i.config,t),i},s.prototype.on=function(t,a){return layui.onevent.call(this,e,t,a)},s.prototype.tabAdd=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.children(".layui-tab-bar"),o=l.children(".layui-tab-content"),r='<li lay-id="'+(i.id||"")+'"'+(i.attr?' lay-attr="'+i.attr+'"':"")+">"+(i.title||"unnaming")+"</li>";return s[0]?s.before(r):n.append(r),o.append('<div class="layui-tab-item">'+(i.content||"")+"</div>"),f.hideTabMore(!0),f.tabAuto(),this},s.prototype.tabDelete=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.find('>li[lay-id="'+i+'"]');return f.tabDelete(null,s),this},s.prototype.tabChange=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.find('>li[lay-id="'+i+'"]');return f.tabClick.call(s[0],null,null,s),this},s.prototype.tab=function(t){t=t||{},b.on("click",t.headerElem,function(i){var e=a(this).index();f.tabClick.call(this,i,e,null,t)})},s.prototype.progress=function(t,i){var e="layui-progress",l=a("."+e+"[lay-filter="+t+"]"),n=l.find("."+e+"-bar"),s=n.find("."+e+"-text");return n.css("width",i),s.text(i),this};var o=".layui-nav",r="layui-nav-item",c="layui-nav-bar",u="layui-nav-tree",d="layui-nav-child",y="layui-nav-more",h="layui-anim layui-anim-upbit",f={tabClick:function(t,i,s,o){o=o||{};var r=s||a(this),i=i||r.parent().children("li").index(r),c=o.headerElem?r.parent():r.parents(".layui-tab").eq(0),u=o.bodyElem?a(o.bodyElem):c.children(".layui-tab-content").children(".layui-tab-item"),d=r.find("a"),y=c.attr("lay-filter");"javascript:;"!==d.attr("href")&&"_blank"===d.attr("target")||(r.addClass(l).siblings().removeClass(l),u.eq(i).addClass(n).siblings().removeClass(n)),layui.event.call(this,e,"tab("+y+")",{elem:c,index:i})},tabDelete:function(t,i){var n=i||a(this).parent(),s=n.index(),o=n.parents(".layui-tab").eq(0),r=o.children(".layui-tab-content").children(".layui-tab-item"),c=o.attr("lay-filter");n.hasClass(l)&&(n.next()[0]?f.tabClick.call(n.next()[0],null,s+1):n.prev()[0]&&f.tabClick.call(n.prev()[0],null,s-1)),n.remove(),r.eq(s).remove(),setTimeout(function(){f.tabAuto()},50),layui.event.call(this,e,"tabDelete("+c+")",{elem:o,index:s})},tabAuto:function(){var t="layui-tab-more",e="layui-tab-bar",l="layui-tab-close",n=this;a(".layui-tab").each(function(){var s=a(this),o=s.children(".layui-tab-title"),r=(s.children(".layui-tab-content").children(".layui-tab-item"),'lay-stope="tabmore"'),c=a('<span class="layui-unselect layui-tab-bar" '+r+"><i "+r+' class="layui-icon">&#xe61a;</i></span>');if(n===window&&8!=i.ie&&f.hideTabMore(!0),s.attr("lay-allowClose")&&o.find("li").each(function(){var t=a(this);if(!t.find("."+l)[0]){var i=a('<i class="layui-icon layui-unselect '+l+'">&#x1006;</i>');i.on("click",f.tabDelete),t.append(i)}}),"string"!=typeof s.attr("lay-unauto"))if(o.prop("scrollWidth")>o.outerWidth()+1){if(o.find("."+e)[0])return;o.append(c),s.attr("overflow",""),c.on("click",function(a){o[this.title?"removeClass":"addClass"](t),this.title=this.title?"":"收缩"})}else o.find("."+e).remove(),s.removeAttr("overflow")})},hideTabMore:function(t){var i=a(".layui-tab-title");t!==!0&&"tabmore"===a(t.target).attr("lay-stope")||(i.removeClass("layui-tab-more"),i.find(".layui-tab-bar").attr("title",""))},clickThis:function(){var t=a(this),i=t.parents(o),n=i.attr("lay-filter"),s=t.parent(),c=t.siblings("."+d),y="string"==typeof s.attr("lay-unselect");"javascript:;"!==t.attr("href")&&"_blank"===t.attr("target")||y||c[0]||(i.find("."+l).removeClass(l),s.addClass(l)),i.hasClass(u)&&(c.removeClass(h),c[0]&&(s["none"===c.css("display")?"addClass":"removeClass"](r+"ed"),"all"===i.attr("lay-shrink")&&s.siblings().removeClass(r+"ed"))),layui.event.call(this,e,"nav("+n+")",t)},collapse:function(){var t=a(this),i=t.find(".layui-colla-icon"),l=t.siblings(".layui-colla-content"),s=t.parents(".layui-collapse").eq(0),o=s.attr("lay-filter"),r="none"===l.css("display");if("string"==typeof s.attr("lay-accordion")){var c=s.children(".layui-colla-item").children("."+n);c.siblings(".layui-colla-title").children(".layui-colla-icon").html("&#xe602;"),c.removeClass(n)}l[r?"addClass":"removeClass"](n),i.html(r?"&#xe61a;":"&#xe602;"),layui.event.call(this,e,"collapse("+o+")",{title:t,content:l,show:r})}};s.prototype.init=function(t,e){var l=function(){return e?'[lay-filter="'+e+'"]':""}(),s={tab:function(){f.tabAuto.call({})},nav:function(){var t=200,e={},s={},p={},b=function(l,o,r){var c=a(this),f=c.find("."+d);o.hasClass(u)?l.css({top:c.position().top,height:c.children("a").outerHeight(),opacity:1}):(f.addClass(h),l.css({left:c.position().left+parseFloat(c.css("marginLeft")),top:c.position().top+c.height()-l.height()}),e[r]=setTimeout(function(){l.css({width:c.width(),opacity:1})},i.ie&&i.ie<10?0:t),clearTimeout(p[r]),"block"===f.css("display")&&clearTimeout(s[r]),s[r]=setTimeout(function(){f.addClass(n),c.find("."+y).addClass(y+"d")},300))};a(o+l).each(function(i){var l=a(this),o=a('<span class="'+c+'"></span>'),h=l.find("."+r);l.find("."+c)[0]||(l.append(o),h.on("mouseenter",function(){b.call(this,o,l,i)}).on("mouseleave",function(){l.hasClass(u)||(clearTimeout(s[i]),s[i]=setTimeout(function(){l.find("."+d).removeClass(n),l.find("."+y).removeClass(y+"d")},300))}),l.on("mouseleave",function(){clearTimeout(e[i]),p[i]=setTimeout(function(){l.hasClass(u)?o.css({height:0,top:o.position().top+o.height()/2,opacity:0}):o.css({width:0,left:o.position().left+o.width()/2,opacity:0})},t)})),h.find("a").each(function(){var t=a(this),i=(t.parent(),t.siblings("."+d));i[0]&&!t.children("."+y)[0]&&t.append('<span class="'+y+'"></span>'),t.off("click",f.clickThis).on("click",f.clickThis)})})},breadcrumb:function(){var t=".layui-breadcrumb";a(t+l).each(function(){var t=a(this),i="lay-separator",e=t.attr(i)||"/",l=t.find("a");l.next("span["+i+"]")[0]||(l.each(function(t){t!==l.length-1&&a(this).after("<span "+i+">"+e+"</span>")}),t.css("visibility","visible"))})},progress:function(){var t="layui-progress";a("."+t+l).each(function(){var i=a(this),e=i.find(".layui-progress-bar"),l=e.attr("lay-percent");e.css("width",function(){return/^.+\/.+$/.test(l)?100*new Function("return "+l)()+"%":l}()),i.attr("lay-showPercent")&&setTimeout(function(){e.html('<span class="'+t+'-text">'+l+"</span>")},350)})},collapse:function(){var t="layui-collapse";a("."+t+l).each(function(){var t=a(this).find(".layui-colla-item");t.each(function(){var t=a(this),i=t.find(".layui-colla-title"),e=t.find(".layui-colla-content"),l="none"===e.css("display");i.find(".layui-colla-icon").remove(),i.append('<i class="layui-icon layui-colla-icon">'+(l?"&#xe602;":"&#xe61a;")+"</i>"),i.off("click",f.collapse).on("click",f.collapse)})})}};return s[t]?s[t]():layui.each(s,function(t,a){a()})},s.prototype.render=s.prototype.init;var p=new s,b=a(document);p.render();var v=".layui-tab-title li";b.on("click",v,f.tabClick),b.on("click",f.hideTabMore),a(window).on("resize",f.tabAuto),t(e,p)});

================================================
FILE: public/layui/lay/modules/flow.js
================================================
/** layui-v2.3.0 MIT License By https://www.layui.com */
 ;layui.define("jquery",function(e){"use strict";var l=layui.$,o=function(e){},t='<i class="layui-anim layui-anim-rotate layui-anim-loop layui-icon ">&#xe63e;</i>';o.prototype.load=function(e){var o,i,n,r,a=this,c=0;e=e||{};var f=l(e.elem);if(f[0]){var m=l(e.scrollElem||document),u=e.mb||50,s=!("isAuto"in e)||e.isAuto,v=e.end||"没有更多了",y=e.scrollElem&&e.scrollElem!==document,d="<cite>加载更多</cite>",h=l('<div class="layui-flow-more"><a href="javascript:;">'+d+"</a></div>");f.find(".layui-flow-more")[0]||f.append(h);var p=function(e,t){e=l(e),h.before(e),t=0==t||null,t?h.html(v):h.find("a").html(d),i=t,o=null,n&&n()},g=function(){o=!0,h.find("a").html(t),"function"==typeof e.done&&e.done(++c,p)};if(g(),h.find("a").on("click",function(){l(this);i||o||g()}),e.isLazyimg)var n=a.lazyimg({elem:e.elem+" img",scrollElem:e.scrollElem});return s?(m.on("scroll",function(){var e=l(this),t=e.scrollTop();r&&clearTimeout(r),i||(r=setTimeout(function(){var i=y?e.height():l(window).height(),n=y?e.prop("scrollHeight"):document.documentElement.scrollHeight;n-t-i<=u&&(o||g())},100))}),a):a}},o.prototype.lazyimg=function(e){var o,t=this,i=0;e=e||{};var n=l(e.scrollElem||document),r=e.elem||"img",a=e.scrollElem&&e.scrollElem!==document,c=function(e,l){var o=n.scrollTop(),r=o+l,c=a?function(){return e.offset().top-n.offset().top+o}():e.offset().top;if(c>=o&&c<=r&&!e.attr("src")){var m=e.attr("lay-src");layui.img(m,function(){var l=t.lazyimg.elem.eq(i);e.attr("src",m).removeAttr("lay-src"),l[0]&&f(l),i++})}},f=function(e,o){var f=a?(o||n).height():l(window).height(),m=n.scrollTop(),u=m+f;if(t.lazyimg.elem=l(r),e)c(e,f);else for(var s=0;s<t.lazyimg.elem.length;s++){var v=t.lazyimg.elem.eq(s),y=a?function(){return v.offset().top-n.offset().top+m}():v.offset().top;if(c(v,f),i=s,y>u)break}};if(f(),!o){var m;n.on("scroll",function(){var e=l(this);m&&clearTimeout(m),m=setTimeout(function(){f(null,e)},50)}),o=!0}return f},e("flow",new o)});

================================================
FILE: public/layui/lay/modules/form.js
================================================
/** layui-v2.3.0 MIT License By https://www.layui.com */
 ;layui.define("layer",function(e){"use strict";var i=layui.$,t=layui.layer,a=layui.hint(),n=layui.device(),l="form",r=".layui-form",s="layui-this",o="layui-hide",c="layui-disabled",u=function(){this.config={verify:{required:[/[\S]+/,"必填项不能为空"],phone:[/^1\d{10}$/,"请输入正确的手机号"],email:[/^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/,"邮箱格式不正确"],url:[/(^#)|(^http(s*):\/\/[^\s]+\.[^\s]+)/,"链接格式不正确"],number:function(e){if(!e||isNaN(e))return"只能填写数字"},date:[/^(\d{4})[-\/](\d{1}|0\d{1}|1[0-2])([-\/](\d{1}|0\d{1}|[1-2][0-9]|3[0-1]))*$/,"日期格式不正确"],identity:[/(^\d{15}$)|(^\d{17}(x|X|\d)$)/,"请输入正确的身份证号"]}}};u.prototype.set=function(e){var t=this;return i.extend(!0,t.config,e),t},u.prototype.verify=function(e){var t=this;return i.extend(!0,t.config.verify,e),t},u.prototype.on=function(e,i){return layui.onevent.call(this,l,e,i)},u.prototype.val=function(e,t){var a=i(r+'[lay-filter="'+e+'"]');a.each(function(e,a){var n=i(this);layui.each(t,function(e,i){var t,a=n.find('[name="'+e+'"]');a[0]&&(t=a[0].type,"checkbox"===t?a[0].checked=i:"radio"===t?a.each(function(){this.value===i&&(this.checked=!0)}):a.val(i))})}),f.render(null,e)},u.prototype.render=function(e,t){var n=this,u=i(r+function(){return t?'[lay-filter="'+t+'"]':""}()),d={select:function(){var e,t="请选择",a="layui-form-select",n="layui-select-title",r="layui-select-none",d="",f=u.find("select"),v=function(t,l){i(t.target).parent().hasClass(n)&&!l||(i("."+a).removeClass(a+"ed "+a+"up"),e&&d&&e.val(d)),e=null},y=function(t,u,f){var y,p=i(this),m=t.find("."+n),k=m.find("input"),g=t.find("dl"),x=g.children("dd"),b=this.selectedIndex;if(!u){var C=function(){var e=t.offset().top+t.outerHeight()+5-h.scrollTop(),i=g.outerHeight();b=p[0].selectedIndex,t.addClass(a+"ed"),x.removeClass(o),y=null,x.eq(b).addClass(s).siblings().removeClass(s),e+i>h.height()&&e>=i&&t.addClass(a+"up")},w=function(e){t.removeClass(a+"ed "+a+"up"),k.blur(),y=null,e||$(k.val(),function(e){e&&(d=g.find("."+s).html(),k&&k.val(d))})};m.on("click",function(e){t.hasClass(a+"ed")?w():(v(e,!0),C()),g.find("."+r).remove()}),m.find(".layui-edge").on("click",function(){k.focus()}),k.on("keyup",function(e){var i=e.keyCode;9===i&&C()}).on("keydown",function(e){var i=e.keyCode;9===i&&w();var t=function(i,a){var n,l;if(e.preventDefault(),a=function(){return a&&a[0]?a:y&&y[0]?y:x.eq(b)}(),l=a[i](),n=a[i]("dd"),l[0]){if(y=a[i](),!n[0]||n.hasClass(c))return t(i,y);n.addClass(s).siblings().removeClass(s);var r=g.children("dd.layui-this"),o=r.position().top,u=g.height(),d=r.height();o>u&&g.scrollTop(o+g.scrollTop()-u+d-5),o<0&&g.scrollTop(o+g.scrollTop())}};38===i&&t("prev"),40===i&&t("next"),13===i&&(e.preventDefault(),g.children("dd."+s).trigger("click"))});var $=function(e,t,a){var n=0;layui.each(x,function(){var t=i(this),l=t.text(),r=l.indexOf(e)===-1;(""===e||"blur"===a?e!==l:r)&&n++,"keyup"===a&&t[r?"addClass":"removeClass"](o)});var l=n===x.length;return t(l),l},T=function(e){var i=this.value,t=e.keyCode;return 9!==t&&13!==t&&37!==t&&38!==t&&39!==t&&40!==t&&($(i,function(e){e?g.find("."+r)[0]||g.append('<p class="'+r+'">无匹配项</p>'):g.find("."+r).remove()},"keyup"),void(""===i&&g.find("."+r).remove()))};f&&k.on("keyup",T).on("blur",function(t){var a=p[0].selectedIndex;e=k,d=i(p[0].options[a]).html(),setTimeout(function(){$(k.val(),function(e){d||k.val("")},"blur")},200)}),x.on("click",function(){var e=i(this),a=e.attr("lay-value"),n=p.attr("lay-filter");return!e.hasClass(c)&&(e.hasClass("layui-select-tips")?k.val(""):(k.val(e.text()),e.addClass(s)),e.siblings().removeClass(s),p.val(a).removeClass("layui-form-danger"),layui.event.call(this,l,"select("+n+")",{elem:p[0],value:a,othis:t}),w(!0),!1)}),t.find("dl>dt").on("click",function(e){return!1}),i(document).off("click",v).on("click",v)}};f.each(function(e,l){var r=i(this),o=r.next("."+a),u=this.disabled,d=l.value,f=i(l.options[l.selectedIndex]),v=l.options[0];if("string"==typeof r.attr("lay-ignore"))return r.show();var h="string"==typeof r.attr("lay-search"),p=v?v.value?t:v.innerHTML||t:t,m=i(['<div class="'+(h?"":"layui-unselect ")+a,(u?" layui-select-disabled":"")+'">','<div class="'+n+'">','<input type="text" placeholder="'+p+'" '+('value="'+(d?f.html():"")+'"')+(h?"":" readonly")+' class="layui-input'+(h?"":" layui-unselect")+(u?" "+c:"")+'">','<i class="layui-edge"></i></div>','<dl class="layui-anim layui-anim-upbit'+(r.find("optgroup")[0]?" layui-select-group":"")+'">',function(e){var i=[];return layui.each(e,function(e,a){0!==e||a.value?"optgroup"===a.tagName.toLowerCase()?i.push("<dt>"+a.label+"</dt>"):i.push('<dd lay-value="'+a.value+'" class="'+(d===a.value?s:"")+(a.disabled?" "+c:"")+'">'+a.innerHTML+"</dd>"):i.push('<dd lay-value="" class="layui-select-tips">'+(a.innerHTML||t)+"</dd>")}),0===i.length&&i.push('<dd lay-value="" class="'+c+'">没有选项</dd>'),i.join("")}(r.find("*"))+"</dl>","</div>"].join(""));o[0]&&o.remove(),r.after(m),y.call(this,m,u,h)})},checkbox:function(){var e={checkbox:["layui-form-checkbox","layui-form-checked","checkbox"],_switch:["layui-form-switch","layui-form-onswitch","switch"]},t=u.find("input[type=checkbox]"),a=function(e,t){var a=i(this);e.on("click",function(){var i=a.attr("lay-filter"),n=(a.attr("lay-text")||"").split("|");a[0].disabled||(a[0].checked?(a[0].checked=!1,e.removeClass(t[1]).find("em").text(n[1])):(a[0].checked=!0,e.addClass(t[1]).find("em").text(n[0])),layui.event.call(a[0],l,t[2]+"("+i+")",{elem:a[0],value:a[0].value,othis:e}))})};t.each(function(t,n){var l=i(this),r=l.attr("lay-skin"),s=(l.attr("lay-text")||"").split("|"),o=this.disabled;"switch"===r&&(r="_"+r);var u=e[r]||e.checkbox;if("string"==typeof l.attr("lay-ignore"))return l.show();var d=l.next("."+u[0]),f=i(['<div class="layui-unselect '+u[0],n.checked?" "+u[1]:"",o?" layui-checkbox-disbaled "+c:"",'"',r?' lay-skin="'+r+'"':"",">",function(){var e=n.title.replace(/\s/g,""),i={checkbox:[e?"<span>"+n.title+"</span>":"",'<i class="layui-icon layui-icon-ok"></i>'].join(""),_switch:"<em>"+((n.checked?s[0]:s[1])||"")+"</em><i></i>"};return i[r]||i.checkbox}(),"</div>"].join(""));d[0]&&d.remove(),l.after(f),a.call(this,f,u)})},radio:function(){var e="layui-form-radio",t=["&#xe643;","&#xe63f;"],a=u.find("input[type=radio]"),n=function(a){var n=i(this),s="layui-anim-scaleSpring";a.on("click",function(){var o=n[0].name,c=n.parents(r),u=n.attr("lay-filter"),d=c.find("input[name="+o.replace(/(\.|#|\[|\])/g,"\\$1")+"]");n[0].disabled||(layui.each(d,function(){var a=i(this).next("."+e);this.checked=!1,a.removeClass(e+"ed"),a.find(".layui-icon").removeClass(s).html(t[1])}),n[0].checked=!0,a.addClass(e+"ed"),a.find(".layui-icon").addClass(s).html(t[0]),layui.event.call(n[0],l,"radio("+u+")",{elem:n[0],value:n[0].value,othis:a}))})};a.each(function(a,l){var r=i(this),s=r.next("."+e),o=this.disabled;if("string"==typeof r.attr("lay-ignore"))return r.show();s[0]&&s.remove();var u=i(['<div class="layui-unselect '+e,l.checked?" "+e+"ed":"",(o?" layui-radio-disbaled "+c:"")+'">','<i class="layui-anim layui-icon">'+t[l.checked?0:1]+"</i>","<div>"+function(){var e=l.title||"";return"string"==typeof r.next().attr("lay-radio")&&(e=r.next().html(),r.next().remove()),e}()+"</div>","</div>"].join(""));r.after(u),n.call(this,u)})}};return e?d[e]?d[e]():a.error("不支持的"+e+"表单渲染"):layui.each(d,function(e,i){i()}),n};var d=function(){var e=i(this),a=f.config.verify,s=null,o="layui-form-danger",c={},u=e.parents(r),d=u.find("*[lay-verify]"),v=e.parents("form")[0],h=u.find("input,select,textarea"),y=e.attr("lay-filter");if(layui.each(d,function(e,l){var r=i(this),c=r.attr("lay-verify").split("|"),u=r.attr("lay-verType"),d=r.val();if(r.removeClass(o),layui.each(c,function(e,i){var c,f="",v="function"==typeof a[i];if(a[i]){var c=v?f=a[i](d,l):!a[i][0].test(d);if(f=f||a[i][1],c)return"tips"===u?t.tips(f,function(){return"string"==typeof r.attr("lay-ignore")||"select"!==l.tagName.toLowerCase()&&!/^checkbox|radio$/.test(l.type)?r:r.next()}(),{tips:1}):"alert"===u?t.alert(f,{title:"提示",shadeClose:!0}):t.msg(f,{icon:5,shift:6}),n.android||n.ios||l.focus(),r.addClass(o),s=!0}}),s)return s}),s)return!1;var p={};return layui.each(h,function(e,i){if(i.name=(i.name||"").replace(/^\s*|\s*&/,""),i.name){if(/^.*\[\]$/.test(i.name)){var t=i.name.match(/^(.*)\[\]$/g)[0];p[t]=0|p[t],i.name=i.name.replace(/^(.*)\[\]$/,"$1["+p[t]++ +"]")}/^checkbox|radio$/.test(i.type)&&!i.checked||(c[i.name]=i.value)}}),layui.event.call(this,l,"submit("+y+")",{elem:this,form:v,field:c})},f=new u,v=i(document),h=i(window);f.render(),v.on("reset",r,function(){var e=i(this).attr("lay-filter");setTimeout(function(){f.render(null,e)},50)}),v.on("submit",r,d).on("click","*[lay-submit]",d),e(l,f)});

================================================
FILE: public/layui/lay/modules/jquery.js
================================================
/** layui-v2.3.0 MIT License By https://www.layui.com */
 ;!function(e,t){"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){function n(e){var t=!!e&&"length"in e&&e.length,n=pe.type(e);return"function"!==n&&!pe.isWindow(e)&&("array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e)}function r(e,t,n){if(pe.isFunction(t))return pe.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return pe.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(Ce.test(t))return pe.filter(t,e,n);t=pe.filter(t,e)}return pe.grep(e,function(e){return pe.inArray(e,t)>-1!==n})}function i(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}function o(e){var t={};return pe.each(e.match(De)||[],function(e,n){t[n]=!0}),t}function a(){re.addEventListener?(re.removeEventListener("DOMContentLoaded",s),e.removeEventListener("load",s)):(re.detachEvent("onreadystatechange",s),e.detachEvent("onload",s))}function s(){(re.addEventListener||"load"===e.event.type||"complete"===re.readyState)&&(a(),pe.ready())}function u(e,t,n){if(void 0===n&&1===e.nodeType){var r="data-"+t.replace(_e,"-$1").toLowerCase();if(n=e.getAttribute(r),"string"==typeof n){try{n="true"===n||"false"!==n&&("null"===n?null:+n+""===n?+n:qe.test(n)?pe.parseJSON(n):n)}catch(i){}pe.data(e,t,n)}else n=void 0}return n}function l(e){var t;for(t in e)if(("data"!==t||!pe.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}function c(e,t,n,r){if(He(e)){var i,o,a=pe.expando,s=e.nodeType,u=s?pe.cache:e,l=s?e[a]:e[a]&&a;if(l&&u[l]&&(r||u[l].data)||void 0!==n||"string"!=typeof t)return l||(l=s?e[a]=ne.pop()||pe.guid++:a),u[l]||(u[l]=s?{}:{toJSON:pe.noop}),"object"!=typeof t&&"function"!=typeof t||(r?u[l]=pe.extend(u[l],t):u[l].data=pe.extend(u[l].data,t)),o=u[l],r||(o.data||(o.data={}),o=o.data),void 0!==n&&(o[pe.camelCase(t)]=n),"string"==typeof t?(i=o[t],null==i&&(i=o[pe.camelCase(t)])):i=o,i}}function f(e,t,n){if(He(e)){var r,i,o=e.nodeType,a=o?pe.cache:e,s=o?e[pe.expando]:pe.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){pe.isArray(t)?t=t.concat(pe.map(t,pe.camelCase)):t in r?t=[t]:(t=pe.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;for(;i--;)delete r[t[i]];if(n?!l(r):!pe.isEmptyObject(r))return}(n||(delete a[s].data,l(a[s])))&&(o?pe.cleanData([e],!0):fe.deleteExpando||a!=a.window?delete a[s]:a[s]=void 0)}}}function d(e,t,n,r){var i,o=1,a=20,s=r?function(){return r.cur()}:function(){return pe.css(e,t,"")},u=s(),l=n&&n[3]||(pe.cssNumber[t]?"":"px"),c=(pe.cssNumber[t]||"px"!==l&&+u)&&Me.exec(pe.css(e,t));if(c&&c[3]!==l){l=l||c[3],n=n||[],c=+u||1;do o=o||".5",c/=o,pe.style(e,t,c+l);while(o!==(o=s()/u)&&1!==o&&--a)}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}function p(e){var t=ze.split("|"),n=e.createDocumentFragment();if(n.createElement)for(;t.length;)n.createElement(t.pop());return n}function h(e,t){var n,r,i=0,o="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):void 0;if(!o)for(o=[],n=e.childNodes||e;null!=(r=n[i]);i++)!t||pe.nodeName(r,t)?o.push(r):pe.merge(o,h(r,t));return void 0===t||t&&pe.nodeName(e,t)?pe.merge([e],o):o}function g(e,t){for(var n,r=0;null!=(n=e[r]);r++)pe._data(n,"globalEval",!t||pe._data(t[r],"globalEval"))}function m(e){Be.test(e.type)&&(e.defaultChecked=e.checked)}function y(e,t,n,r,i){for(var o,a,s,u,l,c,f,d=e.length,y=p(t),v=[],x=0;x<d;x++)if(a=e[x],a||0===a)if("object"===pe.type(a))pe.merge(v,a.nodeType?[a]:a);else if(Ue.test(a)){for(u=u||y.appendChild(t.createElement("div")),l=(We.exec(a)||["",""])[1].toLowerCase(),f=Xe[l]||Xe._default,u.innerHTML=f[1]+pe.htmlPrefilter(a)+f[2],o=f[0];o--;)u=u.lastChild;if(!fe.leadingWhitespace&&$e.test(a)&&v.push(t.createTextNode($e.exec(a)[0])),!fe.tbody)for(a="table"!==l||Ve.test(a)?"<table>"!==f[1]||Ve.test(a)?0:u:u.firstChild,o=a&&a.childNodes.length;o--;)pe.nodeName(c=a.childNodes[o],"tbody")&&!c.childNodes.length&&a.removeChild(c);for(pe.merge(v,u.childNodes),u.textContent="";u.firstChild;)u.removeChild(u.firstChild);u=y.lastChild}else v.push(t.createTextNode(a));for(u&&y.removeChild(u),fe.appendChecked||pe.grep(h(v,"input"),m),x=0;a=v[x++];)if(r&&pe.inArray(a,r)>-1)i&&i.push(a);else if(s=pe.contains(a.ownerDocument,a),u=h(y.appendChild(a),"script"),s&&g(u),n)for(o=0;a=u[o++];)Ie.test(a.type||"")&&n.push(a);return u=null,y}function v(){return!0}function x(){return!1}function b(){try{return re.activeElement}catch(e){}}function w(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)w(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),i===!1)i=x;else if(!i)return e;return 1===o&&(a=i,i=function(e){return pe().off(e),a.apply(this,arguments)},i.guid=a.guid||(a.guid=pe.guid++)),e.each(function(){pe.event.add(this,t,i,r,n)})}function T(e,t){return pe.nodeName(e,"table")&&pe.nodeName(11!==t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function C(e){return e.type=(null!==pe.find.attr(e,"type"))+"/"+e.type,e}function E(e){var t=it.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function N(e,t){if(1===t.nodeType&&pe.hasData(e)){var n,r,i,o=pe._data(e),a=pe._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;r<i;r++)pe.event.add(t,n,s[n][r])}a.data&&(a.data=pe.extend({},a.data))}}function k(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!fe.noCloneEvent&&t[pe.expando]){i=pe._data(t);for(r in i.events)pe.removeEvent(t,r,i.handle);t.removeAttribute(pe.expando)}"script"===n&&t.text!==e.text?(C(t).text=e.text,E(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),fe.html5Clone&&e.innerHTML&&!pe.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Be.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:"input"!==n&&"textarea"!==n||(t.defaultValue=e.defaultValue)}}function S(e,t,n,r){t=oe.apply([],t);var i,o,a,s,u,l,c=0,f=e.length,d=f-1,p=t[0],g=pe.isFunction(p);if(g||f>1&&"string"==typeof p&&!fe.checkClone&&rt.test(p))return e.each(function(i){var o=e.eq(i);g&&(t[0]=p.call(this,i,o.html())),S(o,t,n,r)});if(f&&(l=y(t,e[0].ownerDocument,!1,e,r),i=l.firstChild,1===l.childNodes.length&&(l=i),i||r)){for(s=pe.map(h(l,"script"),C),a=s.length;c<f;c++)o=l,c!==d&&(o=pe.clone(o,!0,!0),a&&pe.merge(s,h(o,"script"))),n.call(e[c],o,c);if(a)for(u=s[s.length-1].ownerDocument,pe.map(s,E),c=0;c<a;c++)o=s[c],Ie.test(o.type||"")&&!pe._data(o,"globalEval")&&pe.contains(u,o)&&(o.src?pe._evalUrl&&pe._evalUrl(o.src):pe.globalEval((o.text||o.textContent||o.innerHTML||"").replace(ot,"")));l=i=null}return e}function A(e,t,n){for(var r,i=t?pe.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||pe.cleanData(h(r)),r.parentNode&&(n&&pe.contains(r.ownerDocument,r)&&g(h(r,"script")),r.parentNode.removeChild(r));return e}function D(e,t){var n=pe(t.createElement(e)).appendTo(t.body),r=pe.css(n[0],"display");return n.detach(),r}function j(e){var t=re,n=lt[e];return n||(n=D(e,t),"none"!==n&&n||(ut=(ut||pe("<iframe frameborder='0' width='0' height='0'/>")).appendTo(t.documentElement),t=(ut[0].contentWindow||ut[0].contentDocument).document,t.write(),t.close(),n=D(e,t),ut.detach()),lt[e]=n),n}function L(e,t){return{get:function(){return e()?void delete this.get:(this.get=t).apply(this,arguments)}}}function H(e){if(e in Et)return e;for(var t=e.charAt(0).toUpperCase()+e.slice(1),n=Ct.length;n--;)if(e=Ct[n]+t,e in Et)return e}function q(e,t){for(var n,r,i,o=[],a=0,s=e.length;a<s;a++)r=e[a],r.style&&(o[a]=pe._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&Re(r)&&(o[a]=pe._data(r,"olddisplay",j(r.nodeName)))):(i=Re(r),(n&&"none"!==n||!i)&&pe._data(r,"olddisplay",i?n:pe.css(r,"display"))));for(a=0;a<s;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}function _(e,t,n){var r=bt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function F(e,t,n,r,i){for(var o=n===(r?"border":"content")?4:
Download .txt
gitextract_dr35u3vo/

├── .browserslistrc
├── .dockerignore
├── .gitignore
├── Dockerfile
├── LICENSE
├── README.md
├── api/
│   └── index.ts
├── deploy.sh
├── docker-compose.yml
├── email.config.d.ts
├── email.config.js
├── index.html
├── jsconfig.json
├── package.json
├── postcss.config.js
├── public/
│   ├── .htaccess
│   ├── index.html
│   ├── layui/
│   │   ├── css/
│   │   │   ├── layui.css
│   │   │   ├── layui.mobile.css
│   │   │   └── modules/
│   │   │       ├── code.css
│   │   │       ├── laydate/
│   │   │       │   └── default/
│   │   │       │       └── laydate.css
│   │   │       └── layer/
│   │   │           └── default/
│   │   │               └── layer.css
│   │   ├── lay/
│   │   │   └── modules/
│   │   │       ├── carousel.js
│   │   │       ├── code.js
│   │   │       ├── element.js
│   │   │       ├── flow.js
│   │   │       ├── form.js
│   │   │       ├── jquery.js
│   │   │       ├── laydate.js
│   │   │       ├── layedit.js
│   │   │       ├── layer.js
│   │   │       ├── laypage.js
│   │   │       ├── laytpl.js
│   │   │       ├── mobile.js
│   │   │       ├── rate.js
│   │   │       ├── table.js
│   │   │       ├── tree.js
│   │   │       ├── upload.js
│   │   │       └── util.js
│   │   ├── layui.all.js
│   │   └── layui.js
│   ├── model/
│   │   └── heartBeat.obj
│   └── three/
│       ├── MeshSurfaceSampler.js
│       ├── OBJLoader.js
│       ├── TrackballControls.js
│       └── simplex-noise.js
├── python_backend/
│   ├── app/
│   │   ├── api/
│   │   │   ├── routers.py
│   │   │   └── v1/
│   │   │       ├── interpreter.py
│   │   │       └── ollama.py
│   │   ├── common/
│   │   │   ├── log.py
│   │   │   ├── open_interpreter.py
│   │   │   └── response.py
│   │   ├── core/
│   │   │   ├── conf.py
│   │   │   └── path_conf.py
│   │   ├── main.py
│   │   ├── requirements.txt
│   │   └── schemas/
│   │       ├── interpreter.py
│   │       └── ollama.py
│   ├── config/
│   │   ├── config.py
│   │   └── config_private.py
│   ├── content.txt
│   ├── main.py
│   ├── requirements.txt
│   └── tools/
│       ├── config_tool.py
│       ├── generate_video.py
│       ├── image_effect.py
│       ├── read_file.py
│       └── stable_api.py
├── src/
│   ├── App.tsx
│   ├── assets/
│   │   └── css/
│   │       ├── ai.scss
│   │       ├── global.css
│   │       ├── gold.scss
│   │       └── markdown.css
│   ├── components/
│   │   ├── ClickEffect.tsx
│   │   ├── Footer.tsx
│   │   ├── HeartBeat.tsx
│   │   ├── Live2DDashboard.tsx
│   │   ├── Navbar.tsx
│   │   ├── VerifyCode.tsx
│   │   ├── VersionUpdateModal.tsx
│   │   ├── goofish/
│   │   │   ├── GoofishLayout.tsx
│   │   │   └── index.ts
│   │   ├── latex/
│   │   │   ├── FileTree.tsx
│   │   │   ├── LatexEditor.tsx
│   │   │   ├── PdfPreview.tsx
│   │   │   └── tree.css
│   │   ├── markmap/
│   │   │   ├── EditNodeModal.tsx
│   │   │   ├── EditorPanel.tsx
│   │   │   ├── InfoModal.tsx
│   │   │   ├── LandscapeMode.tsx
│   │   │   ├── MindmapPanel.tsx
│   │   │   ├── MobileMenu.tsx
│   │   │   ├── MobileTabBar.tsx
│   │   │   └── PromptModal.tsx
│   │   └── video/
│   │       ├── VideoDownload.tsx
│   │       └── VideoParse.tsx
│   ├── goofish/
│   │   ├── ai-tools/
│   │   │   ├── chat-history.tool.ts
│   │   │   ├── index.ts
│   │   │   └── order-query.tool.ts
│   │   ├── api/
│   │   │   ├── index.ts
│   │   │   ├── middlewares/
│   │   │   │   ├── index.ts
│   │   │   │   └── security.middleware.ts
│   │   │   ├── routes/
│   │   │   │   ├── accounts.ts
│   │   │   │   ├── auth.route.ts
│   │   │   │   ├── autoreply.ts
│   │   │   │   ├── autosell.ts
│   │   │   │   ├── conversations.ts
│   │   │   │   ├── dev-messages.route.ts
│   │   │   │   ├── goods.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── logs.ts
│   │   │   │   ├── messages.ts
│   │   │   │   ├── order.route.ts
│   │   │   │   ├── status.ts
│   │   │   │   ├── workflow.route.ts
│   │   │   │   └── ws-push.route.ts
│   │   │   ├── server.ts
│   │   │   └── stores/
│   │   │       ├── conversation.store.ts
│   │   │       └── message.store.ts
│   │   ├── core/
│   │   │   ├── constants.ts
│   │   │   ├── cookies.manager.ts
│   │   │   ├── event-emitter.ts
│   │   │   └── logger.ts
│   │   ├── db/
│   │   │   ├── account.repository.ts
│   │   │   ├── autoreply.repository.ts
│   │   │   ├── autosell.repository.ts
│   │   │   ├── connection.ts
│   │   │   ├── conversation.repository.ts
│   │   │   ├── index.ts
│   │   │   ├── migrations.ts
│   │   │   ├── order.repository.ts
│   │   │   ├── settings.repository.ts
│   │   │   ├── user-avatar.repository.ts
│   │   │   ├── user.repository.ts
│   │   │   └── workflow.repository.ts
│   │   ├── index.ts
│   │   ├── services/
│   │   │   ├── ai.service.ts
│   │   │   ├── auth.service.ts
│   │   │   ├── autoreply.service.ts
│   │   │   ├── autosell.service.ts
│   │   │   ├── conversation.service.ts
│   │   │   ├── goods.service.ts
│   │   │   ├── index.ts
│   │   │   ├── message.service.ts
│   │   │   ├── order.service.ts
│   │   │   ├── user.service.ts
│   │   │   └── workflow.service.ts
│   │   ├── types/
│   │   │   ├── account.types.ts
│   │   │   ├── autoreply.types.ts
│   │   │   ├── autosell.types.ts
│   │   │   ├── common.types.ts
│   │   │   ├── conversation.types.ts
│   │   │   ├── goods.types.ts
│   │   │   ├── index.ts
│   │   │   ├── message.types.ts
│   │   │   ├── order.types.ts
│   │   │   ├── user.types.ts
│   │   │   └── workflow.types.ts
│   │   ├── utils/
│   │   │   ├── cookies.ts
│   │   │   ├── crypto.ts
│   │   │   ├── date.ts
│   │   │   ├── index.ts
│   │   │   ├── jwt.util.ts
│   │   │   ├── msgpack.ts
│   │   │   └── password.util.ts
│   │   └── websocket/
│   │       ├── client.manager.ts
│   │       ├── client.ts
│   │       ├── index.ts
│   │       ├── message.parser.ts
│   │       ├── message.receiver.ts
│   │       ├── message.sender.ts
│   │       └── token.ts
│   ├── hooks/
│   │   ├── goofish/
│   │   │   ├── index.ts
│   │   │   └── useWebSocket.ts
│   │   └── useChangeLanguage.ts
│   ├── i18n/
│   │   ├── config.ts
│   │   └── locales/
│   │       ├── en/
│   │       │   ├── about.ts
│   │       │   ├── aimarkmap.ts
│   │       │   ├── cartoon.ts
│   │       │   ├── cartoonChapter.ts
│   │       │   ├── cartoonDetail.ts
│   │       │   ├── common.ts
│   │       │   ├── gold.ts
│   │       │   ├── goofish.ts
│   │       │   ├── gpt.ts
│   │       │   ├── help.ts
│   │       │   ├── home.ts
│   │       │   ├── latex.ts
│   │       │   ├── login.ts
│   │       │   ├── music.ts
│   │       │   ├── nav.ts
│   │       │   ├── notFound.ts
│   │       │   ├── paper.ts
│   │       │   ├── textToPhoto.ts
│   │       │   ├── trans.ts
│   │       │   ├── versionUpdate.ts
│   │       │   └── video.ts
│   │       ├── en.ts
│   │       ├── zh/
│   │       │   ├── about.ts
│   │       │   ├── aimarkmap.ts
│   │       │   ├── cartoon.ts
│   │       │   ├── cartoonChapter.ts
│   │       │   ├── cartoonDetail.ts
│   │       │   ├── common.ts
│   │       │   ├── gold.ts
│   │       │   ├── goofish.ts
│   │       │   ├── gpt.ts
│   │       │   ├── help.ts
│   │       │   ├── home.ts
│   │       │   ├── latex.ts
│   │       │   ├── login.ts
│   │       │   ├── music.ts
│   │       │   ├── nav.ts
│   │       │   ├── notFound.ts
│   │       │   ├── paper.ts
│   │       │   ├── textToPhoto.ts
│   │       │   ├── trans.ts
│   │       │   ├── versionUpdate.ts
│   │       │   └── video.ts
│   │       └── zh.ts
│   ├── index.css
│   ├── main.tsx
│   ├── pages/
│   │   ├── About.tsx
│   │   ├── Cartoon.tsx
│   │   ├── CartoonChapter.tsx
│   │   ├── CartoonDetail.tsx
│   │   ├── GPT.tsx
│   │   ├── Gold.tsx
│   │   ├── Help.tsx
│   │   ├── Home.tsx
│   │   ├── Latex.tsx
│   │   ├── Login.tsx
│   │   ├── Markmap.tsx
│   │   ├── Music.tsx
│   │   ├── NotFound.tsx
│   │   ├── PaperListPage.tsx
│   │   ├── TextToPhoto.tsx
│   │   ├── Trans.tsx
│   │   ├── Video.tsx
│   │   ├── WorkerAgent.tsx
│   │   └── goofish/
│   │       ├── Accounts/
│   │       │   └── index.tsx
│   │       ├── AutoReply/
│   │       │   └── index.tsx
│   │       ├── AutoSell/
│   │       │   └── index.tsx
│   │       ├── Conversations/
│   │       │   └── index.tsx
│   │       ├── Dashboard/
│   │       │   └── index.tsx
│   │       ├── Goods/
│   │       │   └── index.tsx
│   │       ├── Logs/
│   │       │   └── index.tsx
│   │       ├── Orders/
│   │       │   └── index.tsx
│   │       └── Workflow/
│   │           └── index.tsx
│   ├── sdk/
│   │   ├── analytics/
│   │   │   ├── README.md
│   │   │   ├── examples.tsx
│   │   │   ├── hooks.ts
│   │   │   ├── index.ts
│   │   │   └── types.d.ts
│   │   ├── fingerprint/
│   │   │   ├── README.md
│   │   │   ├── collector.ts
│   │   │   ├── examples.tsx
│   │   │   ├── hash.ts
│   │   │   ├── hooks.ts
│   │   │   ├── index.ts
│   │   │   ├── storage.ts
│   │   │   └── types.ts
│   │   └── index.ts
│   ├── services/
│   │   ├── goofish/
│   │   │   └── index.ts
│   │   ├── latexApi.ts
│   │   ├── server.ts
│   │   └── turnstile.ts
│   ├── types/
│   │   ├── goofish/
│   │   │   └── index.ts
│   │   └── turnstile.d.ts
│   ├── utils/
│   │   ├── backTop.ts
│   │   ├── checkIp.ts
│   │   ├── images.ts
│   │   ├── isMobile.ts
│   │   ├── markmap.ts
│   │   ├── paper.ts
│   │   ├── price.ts
│   │   ├── token.ts
│   │   └── versionChecker.ts
│   ├── version.d.ts
│   └── vite-env.d.ts
├── tailwind.config.js
├── tsconfig.json
├── tsconfig.node.json
├── vercel.json
└── vite.config.ts
Download .txt
SYMBOL INDEX (919 symbols across 153 files)

FILE: api/index.ts
  constant JWT_SECRET (line 12) | const JWT_SECRET = 'chattyplay-jwt-secret-2024'
  constant PASSWORD_SECRET (line 13) | const PASSWORD_SECRET = 'chattyplay-secret-key-2024'
  constant TOKEN_EXPIRY (line 14) | const TOKEN_EXPIRY = 7 * 24 * 60 * 60 * 1000 // 7天
  constant REDIS_REST_URL (line 17) | const REDIS_REST_URL = process.env.UPSTASH_REDIS_REST_URL
  constant REDIS_REST_TOKEN (line 18) | const REDIS_REST_TOKEN = process.env.UPSTASH_REDIS_REST_TOKEN
  constant USER_COUNTER_KEY (line 21) | const USER_COUNTER_KEY = 'user:id_counter'
  constant USER_HASH_PREFIX (line 23) | const USER_HASH_PREFIX = 'user:'
  constant USERNAME_INDEX_KEY (line 25) | const USERNAME_INDEX_KEY = 'user:username_index'
  type RedisResult (line 29) | interface RedisResult {
  function redisCommand (line 34) | async function redisCommand(command: string[]): Promise<RedisResult> {
  function getNextUserId (line 61) | async function getNextUserId(): Promise<number | null> {
  function getUserById (line 67) | async function getUserById(id: number): Promise<User | null> {
  function getUserByUsername (line 80) | async function getUserByUsername(username: string): Promise<User | null> {
  function getUserByEmail (line 87) | async function getUserByEmail(email: string): Promise<User | null> {
  function createUser (line 101) | async function createUser(user: User): Promise<void> {
  function updateUserField (line 117) | async function updateUserField(id: number, field: string, value: string)...
  type User (line 123) | interface User {
  type UserWithoutPassword (line 133) | interface UserWithoutPassword {
  function hashPassword (line 144) | function hashPassword(password: string): string {
  function verifyPassword (line 148) | function verifyPassword(password: string, hashedPassword: string): boole...
  function generateToken (line 155) | function generateToken(payload: { userId: number; username: string }): s...
  function verifyToken (line 174) | function verifyToken(token: string): { userId: number; username: string ...
  function base64UrlEncode (line 202) | function base64UrlEncode(str: string): string {
  function base64UrlDecode (line 209) | function base64UrlDecode(str: string): string {
  method start (line 548) | async start(controller) {
  method start (line 608) | async start(controller) {

FILE: email.config.d.ts
  type EmailConfig (line 1) | interface EmailConfig {

FILE: public/layui/lay/modules/jquery.js
  function n (line 2) | function n(e){var t=!!e&&"length"in e&&e.length,n=pe.type(e);return"func...
  function r (line 2) | function r(e,t,n){if(pe.isFunction(t))return pe.grep(e,function(e,r){ret...
  function i (line 2) | function i(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}
  function o (line 2) | function o(e){var t={};return pe.each(e.match(De)||[],function(e,n){t[n]...
  function a (line 2) | function a(){re.addEventListener?(re.removeEventListener("DOMContentLoad...
  function s (line 2) | function s(){(re.addEventListener||"load"===e.event.type||"complete"===r...
  function u (line 2) | function u(e,t,n){if(void 0===n&&1===e.nodeType){var r="data-"+t.replace...
  function l (line 2) | function l(e){var t;for(t in e)if(("data"!==t||!pe.isEmptyObject(e[t]))&...
  function c (line 2) | function c(e,t,n,r){if(He(e)){var i,o,a=pe.expando,s=e.nodeType,u=s?pe.c...
  function f (line 2) | function f(e,t,n){if(He(e)){var r,i,o=e.nodeType,a=o?pe.cache:e,s=o?e[pe...
  function d (line 2) | function d(e,t,n,r){var i,o=1,a=20,s=r?function(){return r.cur()}:functi...
  function p (line 2) | function p(e){var t=ze.split("|"),n=e.createDocumentFragment();if(n.crea...
  function h (line 2) | function h(e,t){var n,r,i=0,o="undefined"!=typeof e.getElementsByTagName...
  function g (line 2) | function g(e,t){for(var n,r=0;null!=(n=e[r]);r++)pe._data(n,"globalEval"...
  function m (line 2) | function m(e){Be.test(e.type)&&(e.defaultChecked=e.checked)}
  function y (line 2) | function y(e,t,n,r,i){for(var o,a,s,u,l,c,f,d=e.length,y=p(t),v=[],x=0;x...
  function v (line 2) | function v(){return!0}
  function x (line 2) | function x(){return!1}
  function b (line 2) | function b(){try{return re.activeElement}catch(e){}}
  function w (line 2) | function w(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof ...
  function T (line 2) | function T(e,t){return pe.nodeName(e,"table")&&pe.nodeName(11!==t.nodeTy...
  function C (line 2) | function C(e){return e.type=(null!==pe.find.attr(e,"type"))+"/"+e.type,e}
  function E (line 2) | function E(e){var t=it.exec(e.type);return t?e.type=t[1]:e.removeAttribu...
  function N (line 2) | function N(e,t){if(1===t.nodeType&&pe.hasData(e)){var n,r,i,o=pe._data(e...
  function k (line 2) | function k(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase...
  function S (line 2) | function S(e,t,n,r){t=oe.apply([],t);var i,o,a,s,u,l,c=0,f=e.length,d=f-...
  function A (line 2) | function A(e,t,n){for(var r,i=t?pe.filter(t,e):e,o=0;null!=(r=i[o]);o++)...
  function D (line 2) | function D(e,t){var n=pe(t.createElement(e)).appendTo(t.body),r=pe.css(n...
  function j (line 2) | function j(e){var t=re,n=lt[e];return n||(n=D(e,t),"none"!==n&&n||(ut=(u...
  function L (line 2) | function L(e,t){return{get:function(){return e()?void delete this.get:(t...
  function H (line 2) | function H(e){if(e in Et)return e;for(var t=e.charAt(0).toUpperCase()+e....
  function q (line 2) | function q(e,t){for(var n,r,i,o=[],a=0,s=e.length;a<s;a++)r=e[a],r.style...
  function _ (line 2) | function _(e,t,n){var r=bt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2...
  function F (line 2) | function F(e,t,n,r,i){for(var o=n===(r?"border":"content")?4:"width"===t...
  function M (line 2) | function M(t,n,r){var i=!0,o="width"===n?t.offsetWidth:t.offsetHeight,a=...
  function O (line 2) | function O(e,t,n,r,i){return new O.prototype.init(e,t,n,r,i)}
  function R (line 2) | function R(){return e.setTimeout(function(){Nt=void 0}),Nt=pe.now()}
  function P (line 2) | function P(e,t){var n,r={height:e},i=0;for(t=t?1:0;i<4;i+=2-t)n=Oe[i],r[...
  function B (line 2) | function B(e,t,n){for(var r,i=($.tweeners[t]||[]).concat($.tweeners["*"]...
  function W (line 2) | function W(e,t,n){var r,i,o,a,s,u,l,c,f=this,d={},p=e.style,h=e.nodeType...
  function I (line 2) | function I(e,t){var n,r,i,o,a;for(n in e)if(r=pe.camelCase(n),i=t[r],o=e...
  function $ (line 2) | function $(e,t,n){var r,i,o=0,a=$.prefilters.length,s=pe.Deferred().alwa...
  function z (line 2) | function z(e){return pe.attr(e,"class")||""}
  function X (line 2) | function X(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r...
  function U (line 2) | function U(e,t,n,r){function i(s){var u;return o[s]=!0,pe.each(e[s]||[],...
  function V (line 2) | function V(e,t){var n,r,i=pe.ajaxSettings.flatOptions||{};for(r in t)voi...
  function Y (line 2) | function Y(e,t,n){for(var r,i,o,a,s=e.contents,u=e.dataTypes;"*"===u[0];...
  function J (line 2) | function J(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for...
  function G (line 2) | function G(e){return e.style&&e.style.display||pe.css(e,"display")}
  function K (line 2) | function K(e){for(;e&&1===e.nodeType;){if("none"===G(e)||"hidden"===e.ty...
  function Q (line 2) | function Q(e,t,n,r){var i;if(pe.isArray(t))pe.each(t,function(t,i){n||rn...
  function Z (line 2) | function Z(){try{return new e.XMLHttpRequest}catch(t){}}
  function ee (line 2) | function ee(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(...
  function te (line 2) | function te(e){return pe.isWindow(e)?e:9===e.nodeType&&(e.defaultView||e...
  function t (line 2) | function t(e,t,n,r){var i,o,a,s,u,l,f,p,h=t&&t.ownerDocument,g=t?t.nodeT...
  function n (line 2) | function n(){function e(n,r){return t.push(n+" ")>T.cacheLength&&delete ...
  function r (line 2) | function r(e){return e[P]=!0,e}
  function i (line 2) | function i(e){var t=H.createElement("div");try{return!!e(t)}catch(n){ret...
  function o (line 2) | function o(e,t){for(var n=e.split("|"),r=n.length;r--;)T.attrHandle[n[r]...
  function a (line 2) | function a(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sour...
  function s (line 2) | function s(e){return function(t){var n=t.nodeName.toLowerCase();return"i...
  function u (line 2) | function u(e){return function(t){var n=t.nodeName.toLowerCase();return("...
  function l (line 2) | function l(e){return r(function(t){return t=+t,r(function(n,r){for(var i...
  function c (line 2) | function c(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}
  function f (line 2) | function f(){}
  function d (line 2) | function d(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}
  function p (line 2) | function p(e,t,n){var r=t.dir,i=n&&"parentNode"===r,o=I++;return t.first...
  function h (line 2) | function h(e){return e.length>1?function(t,n,r){for(var i=e.length;i--;)...
  function g (line 2) | function g(e,n,r){for(var i=0,o=n.length;i<o;i++)t(e,n[i],r);return r}
  function m (line 2) | function m(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o...
  function y (line 2) | function y(e,t,n,i,o,a){return i&&!i[P]&&(i=y(i)),o&&!o[P]&&(o=y(o,a)),r...
  function v (line 2) | function v(e){for(var t,n,r,i=e.length,o=T.relative[e[0].type],a=o||T.re...
  function x (line 2) | function x(e,n){var i=n.length>0,o=e.length>0,a=function(r,a,s,u,l){var ...
  function t (line 4) | function t(){var t,c,f=re.documentElement;f.appendChild(u),l.style.cssTe...
  function r (line 4) | function r(t,n,r,i){var o,f,v,x,w,C=n;2!==b&&(b=2,u&&e.clearTimeout(u),c...

FILE: public/layui/lay/modules/layer.js
  function e (line 2) | function e(){var e=a.cancel&&a.cancel(t.index,n);e===!1||r.close(t.index)}
  function o (line 2) | function o(e,t,i){var n=new Image;return n.src=e,n.complete?t(n):(n.onlo...

FILE: public/layui/lay/modules/mobile.js
  function t (line 2) | function t(t){return null==t?String(t):J[W.call(t)]||"object"}
  function e (line 2) | function e(e){return"function"==t(e)}
  function n (line 2) | function n(t){return null!=t&&t==t.window}
  function r (line 2) | function r(t){return null!=t&&t.nodeType==t.DOCUMENT_NODE}
  function i (line 2) | function i(e){return"object"==t(e)}
  function o (line 2) | function o(t){return i(t)&&!n(t)&&Object.getPrototypeOf(t)==Object.proto...
  function a (line 2) | function a(t){var e=!!t&&"length"in t&&t.length,r=T.type(t);return"funct...
  function s (line 2) | function s(t){return A.call(t,function(t){return null!=t})}
  function u (line 2) | function u(t){return t.length>0?T.fn.concat.apply([],t):t}
  function c (line 2) | function c(t){return t.replace(/::/g,"/").replace(/([A-Z]+)([A-Z][a-z])/...
  function l (line 2) | function l(t){return t in F?F[t]:F[t]=new RegExp("(^|\\s)"+t+"(\\s|$)")}
  function f (line 2) | function f(t,e){return"number"!=typeof e||k[c(t)]?e:e+"px"}
  function h (line 2) | function h(t){var e,n;return $[t]||(e=L.createElement(t),L.body.appendCh...
  function p (line 2) | function p(t){return"children"in t?D.call(t.children):T.map(t.childNodes...
  function d (line 2) | function d(t,e){var n,r=t?t.length:0;for(n=0;n<r;n++)this[n]=t[n];this.l...
  function m (line 2) | function m(t,e,n){for(j in e)n&&(o(e[j])||Q(e[j]))?(o(e[j])&&!o(t[j])&&(...
  function v (line 2) | function v(t,e){return null==e?T(t):T(t).filter(e)}
  function g (line 2) | function g(t,n,r,i){return e(n)?n.call(t,r,i):n}
  function y (line 2) | function y(t,e,n){null==n?t.removeAttribute(e):t.setAttribute(e,n)}
  function x (line 2) | function x(t,e){var n=t.className||"",r=n&&n.baseVal!==E;return e===E?r?...
  function b (line 2) | function b(t){try{return t?"true"==t||"false"!=t&&("null"==t?null:+t+""=...
  function w (line 2) | function w(t,e){e(t);for(var n=0,r=t.childNodes.length;n<r;n++)w(t.child...
  function e (line 2) | function e(t){return t._zid||(t._zid=h++)}
  function n (line 2) | function n(t,n,o,a){if(n=r(n),n.ns)var s=i(n.ns);return(v[e(t)]||[]).fil...
  function r (line 2) | function r(t){var e=(""+t).split(".");return{e:e[0],ns:e.slice(1).sort()...
  function i (line 2) | function i(t){return new RegExp("(?:^| )"+t.replace(" "," .* ?")+"(?: |$...
  function o (line 2) | function o(t,e){return t.del&&!y&&t.e in x||!!e}
  function a (line 2) | function a(t){return b[t]||y&&x[t]||t}
  function s (line 2) | function s(n,i,s,u,l,h,p){var d=e(n),m=v[d]||(v[d]=[]);i.split(/\s/).for...
  function u (line 2) | function u(t,r,i,s,u){var c=e(t);(r||"").split(/\s/).forEach(function(e)...
  function c (line 2) | function c(e,n){return!n&&e.isDefaultPrevented||(n||(n=e),t.each(T,funct...
  function l (line 2) | function l(t){var e,n={originalEvent:t};for(e in t)j.test(e)||t[e]===f||...
  function e (line 2) | function e(e,n,r){var i=t.Event(n);return t(e).trigger(i,r),!i.isDefault...
  function n (line 2) | function n(t,n,r,i){if(t.global)return e(n||x,r,i)}
  function r (line 2) | function r(e){e.global&&0===t.active++&&n(e,null,"ajaxStart")}
  function i (line 2) | function i(e){e.global&&!--t.active&&n(e,null,"ajaxStop")}
  function o (line 2) | function o(t,e){var r=e.context;return e.beforeSend.call(r,t,e)!==!1&&n(...
  function a (line 2) | function a(t,e,r,i){var o=r.context,a="success";r.success.call(o,t,a,e),...
  function s (line 2) | function s(t,e,r,i,o){var a=i.context;i.error.call(a,r,e,t),o&&o.rejectW...
  function u (line 2) | function u(t,e,r){var o=r.context;r.complete.call(o,e,t),n(r,o,"ajaxComp...
  function c (line 2) | function c(t,e,n){if(n.dataFilter==l)return t;var r=n.context;return n.d...
  function l (line 2) | function l(){}
  function f (line 2) | function f(t){return t&&(t=t.split(";",2)[0]),t&&(t==T?"html":t==j?"json...
  function h (line 2) | function h(t,e){return""==e?t:(t+"&"+e).replace(/[&?]{1,2}/,"?")}
  function p (line 2) | function p(e){e.processData&&e.data&&"string"!=t.type(e.data)&&(e.data=t...
  function d (line 2) | function d(e,n,r,i){return t.isFunction(n)&&(i=r,r=n,n=void 0),t.isFunct...
  function m (line 2) | function m(e,n,r,i){var o,a=t.isArray(n),s=t.isPlainObject(n);t.each(n,f...

FILE: public/layui/layui.all.js
  function s (line 2) | function s(e,t){var o="PLaySTATION 3"===navigator.platform?/^complete$/:...
  function c (line 2) | function c(){l.push(layui[d]),e.length>1?y.use(e.slice(1),o,l):"function...
  function n (line 2) | function n(e){var t=!!e&&"length"in e&&e.length,n=pe.type(e);return"func...
  function r (line 2) | function r(e,t,n){if(pe.isFunction(t))return pe.grep(e,function(e,r){ret...
  function i (line 2) | function i(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}
  function o (line 2) | function o(e){var t={};return pe.each(e.match(De)||[],function(e,n){t[n]...
  function a (line 2) | function a(){re.addEventListener?(re.removeEventListener("DOMContentLoad...
  function s (line 2) | function s(){(re.addEventListener||"load"===e.event.type||"complete"===r...
  function u (line 2) | function u(e,t,n){if(void 0===n&&1===e.nodeType){var r="data-"+t.replace...
  function l (line 2) | function l(e){var t;for(t in e)if(("data"!==t||!pe.isEmptyObject(e[t]))&...
  function c (line 2) | function c(e,t,n,r){if(He(e)){var i,o,a=pe.expando,s=e.nodeType,u=s?pe.c...
  function f (line 2) | function f(e,t,n){if(He(e)){var r,i,o=e.nodeType,a=o?pe.cache:e,s=o?e[pe...
  function d (line 2) | function d(e,t,n,r){var i,o=1,a=20,s=r?function(){return r.cur()}:functi...
  function p (line 2) | function p(e){var t=ze.split("|"),n=e.createDocumentFragment();if(n.crea...
  function h (line 2) | function h(e,t){var n,r,i=0,o="undefined"!=typeof e.getElementsByTagName...
  function g (line 2) | function g(e,t){for(var n,r=0;null!=(n=e[r]);r++)pe._data(n,"globalEval"...
  function m (line 2) | function m(e){Be.test(e.type)&&(e.defaultChecked=e.checked)}
  function y (line 2) | function y(e,t,n,r,i){for(var o,a,s,u,l,c,f,d=e.length,y=p(t),v=[],x=0;x...
  function v (line 2) | function v(){return!0}
  function x (line 2) | function x(){return!1}
  function b (line 2) | function b(){try{return re.activeElement}catch(e){}}
  function w (line 2) | function w(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof ...
  function T (line 2) | function T(e,t){return pe.nodeName(e,"table")&&pe.nodeName(11!==t.nodeTy...
  function C (line 2) | function C(e){return e.type=(null!==pe.find.attr(e,"type"))+"/"+e.type,e}
  function E (line 2) | function E(e){var t=it.exec(e.type);return t?e.type=t[1]:e.removeAttribu...
  function N (line 2) | function N(e,t){if(1===t.nodeType&&pe.hasData(e)){var n,r,i,o=pe._data(e...
  function k (line 2) | function k(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase...
  function S (line 2) | function S(e,t,n,r){t=oe.apply([],t);var i,o,a,s,u,l,c=0,f=e.length,d=f-...
  function A (line 2) | function A(e,t,n){for(var r,i=t?pe.filter(t,e):e,o=0;null!=(r=i[o]);o++)...
  function D (line 2) | function D(e,t){var n=pe(t.createElement(e)).appendTo(t.body),r=pe.css(n...
  function j (line 2) | function j(e){var t=re,n=lt[e];return n||(n=D(e,t),"none"!==n&&n||(ut=(u...
  function L (line 2) | function L(e,t){return{get:function(){return e()?void delete this.get:(t...
  function H (line 2) | function H(e){if(e in Et)return e;for(var t=e.charAt(0).toUpperCase()+e....
  function q (line 2) | function q(e,t){for(var n,r,i,o=[],a=0,s=e.length;a<s;a++)r=e[a],r.style...
  function _ (line 2) | function _(e,t,n){var r=bt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2...
  function F (line 2) | function F(e,t,n,r,i){for(var o=n===(r?"border":"content")?4:"width"===t...
  function M (line 2) | function M(t,n,r){var i=!0,o="width"===n?t.offsetWidth:t.offsetHeight,a=...
  function O (line 2) | function O(e,t,n,r,i){return new O.prototype.init(e,t,n,r,i)}
  function R (line 2) | function R(){return e.setTimeout(function(){Nt=void 0}),Nt=pe.now()}
  function P (line 2) | function P(e,t){var n,r={height:e},i=0;for(t=t?1:0;i<4;i+=2-t)n=Oe[i],r[...
  function B (line 2) | function B(e,t,n){for(var r,i=($.tweeners[t]||[]).concat($.tweeners["*"]...
  function W (line 2) | function W(e,t,n){var r,i,o,a,s,u,l,c,f=this,d={},p=e.style,h=e.nodeType...
  function I (line 2) | function I(e,t){var n,r,i,o,a;for(n in e)if(r=pe.camelCase(n),i=t[r],o=e...
  function $ (line 2) | function $(e,t,n){var r,i,o=0,a=$.prefilters.length,s=pe.Deferred().alwa...
  function z (line 2) | function z(e){return pe.attr(e,"class")||""}
  function X (line 2) | function X(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r...
  function U (line 2) | function U(e,t,n,r){function i(s){var u;return o[s]=!0,pe.each(e[s]||[],...
  function V (line 2) | function V(e,t){var n,r,i=pe.ajaxSettings.flatOptions||{};for(r in t)voi...
  function Y (line 2) | function Y(e,t,n){for(var r,i,o,a,s=e.contents,u=e.dataTypes;"*"===u[0];...
  function J (line 2) | function J(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for...
  function G (line 2) | function G(e){return e.style&&e.style.display||pe.css(e,"display")}
  function K (line 2) | function K(e){for(;e&&1===e.nodeType;){if("none"===G(e)||"hidden"===e.ty...
  function Q (line 2) | function Q(e,t,n,r){var i;if(pe.isArray(t))pe.each(t,function(t,i){n||rn...
  function Z (line 2) | function Z(){try{return new e.XMLHttpRequest}catch(t){}}
  function ee (line 2) | function ee(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(...
  function te (line 2) | function te(e){return pe.isWindow(e)?e:9===e.nodeType&&(e.defaultView||e...
  function t (line 2) | function t(e,t,n,r){var i,o,a,s,u,l,f,p,h=t&&t.ownerDocument,g=t?t.nodeT...
  function n (line 2) | function n(){function e(n,r){return t.push(n+" ")>T.cacheLength&&delete ...
  function r (line 2) | function r(e){return e[P]=!0,e}
  function i (line 2) | function i(e){var t=H.createElement("div");try{return!!e(t)}catch(n){ret...
  function o (line 2) | function o(e,t){for(var n=e.split("|"),r=n.length;r--;)T.attrHandle[n[r]...
  function a (line 2) | function a(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sour...
  function s (line 2) | function s(e){return function(t){var n=t.nodeName.toLowerCase();return"i...
  function u (line 2) | function u(e){return function(t){var n=t.nodeName.toLowerCase();return("...
  function l (line 2) | function l(e){return r(function(t){return t=+t,r(function(n,r){for(var i...
  function c (line 2) | function c(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}
  function f (line 2) | function f(){}
  function d (line 2) | function d(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}
  function p (line 2) | function p(e,t,n){var r=t.dir,i=n&&"parentNode"===r,o=I++;return t.first...
  function h (line 2) | function h(e){return e.length>1?function(t,n,r){for(var i=e.length;i--;)...
  function g (line 2) | function g(e,n,r){for(var i=0,o=n.length;i<o;i++)t(e,n[i],r);return r}
  function m (line 2) | function m(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o...
  function y (line 2) | function y(e,t,n,i,o,a){return i&&!i[P]&&(i=y(i)),o&&!o[P]&&(o=y(o,a)),r...
  function v (line 2) | function v(e){for(var t,n,r,i=e.length,o=T.relative[e[0].type],a=o||T.re...
  function x (line 2) | function x(e,n){var i=n.length>0,o=e.length>0,a=function(r,a,s,u,l){var ...
  function t (line 4) | function t(){var t,c,f=re.documentElement;f.appendChild(u),l.style.cssTe...
  function r (line 4) | function r(t,n,r,i){var o,f,v,x,w,C=n;2!==b&&(b=2,u&&e.clearTimeout(u),c...
  function e (line 5) | function e(){var e=a.cancel&&a.cancel(t.index,n);e===!1||r.close(t.index)}
  function o (line 5) | function o(e,t,i){var n=new Image;return n.src=e,n.complete?t(n):(n.onlo...

FILE: public/layui/layui.js
  function s (line 2) | function s(e,t){var o="PLaySTATION 3"===navigator.platform?/^complete$/:...
  function c (line 2) | function c(){l.push(layui[d]),e.length>1?y.use(e.slice(1),o,l):"function...

FILE: public/three/MeshSurfaceSampler.js
  class MeshSurfaceSampler (line 18) | class MeshSurfaceSampler {
    method constructor (line 20) | constructor( mesh ) {
    method setWeightAttribute (line 46) | setWeightAttribute( name ) {
    method build (line 53) | build() {
    method setRandomGenerator (line 96) | setRandomGenerator( randomFunction ) {
    method sample (line 103) | sample( targetPosition, targetNormal, targetColor ) {
    method binarySearch (line 111) | binarySearch( x ) {
    method sampleFace (line 143) | sampleFace( faceIndex, targetPosition, targetNormal, targetColor ) {

FILE: public/three/OBJLoader.js
  function ParserState (line 21) | function ParserState() {
  class OBJLoader (line 371) | class OBJLoader extends THREE.Loader {
    method constructor (line 373) | constructor( manager ) {
    method load (line 380) | load( url, onLoad, onProgress, onError ) {
    method setMaterials (line 413) | setMaterials( materials ) {
    method parse (line 420) | parse( text ) {

FILE: public/three/TrackballControls.js
  class TrackballControls (line 13) | class TrackballControls extends THREE.EventDispatcher {
    method constructor (line 15) | constructor( object, domElement ) {

FILE: public/three/simplex-noise.js
  function SimplexNoise (line 39) | function SimplexNoise(randomOrSeed) {
  function buildPermutationTable (line 393) | function buildPermutationTable(random) {
  function alea (line 414) | function alea() {
  function masher (line 447) | function masher() {

FILE: python_backend/app/api/v1/interpreter.py
  function interpreter_chat (line 13) | def interpreter_chat(message: InterpreterMessage) -> ResponseModel:
  function interpreter_chat_stream (line 19) | def interpreter_chat_stream(message: InterpreterMessage) -> StreamingRes...
  function interpreter_history (line 28) | def interpreter_history() -> ResponseModel:
  function interpreter_reset (line 33) | def interpreter_reset() -> ResponseModel:

FILE: python_backend/app/api/v1/ollama.py
  function ollama_chat (line 15) | async def ollama_chat(messages: OllamaMessages) -> ResponseModel:
  function ollama_chat_stream (line 23) | async def ollama_chat_stream(messages: OllamaMessages):
  function ollama_list (line 37) | async def ollama_list() -> ResponseModel:
  function ollama_pull (line 43) | async def ollama_pull(model: Annotated[str, Query()]) -> ResponseModel:
  function ollama_delete (line 49) | async def ollama_delete(model: Annotated[str, Query()]) -> ResponseModel:

FILE: python_backend/app/common/log.py
  class Logger (line 9) | class Logger:
    method log (line 11) | def log() -> loguru.Logger:

FILE: python_backend/app/common/open_interpreter.py
  class OpenInterpreter (line 5) | class OpenInterpreter(_OpenInterpreter):
    method __init__ (line 6) | def __init__(self):

FILE: python_backend/app/common/response.py
  class ResponseModel (line 5) | class ResponseModel(BaseModel):

FILE: python_backend/app/core/conf.py
  class Settings (line 7) | class Settings(BaseSettings):
  function get_settings (line 38) | def get_settings():

FILE: python_backend/app/main.py
  function openai_auth_exception_handler (line 24) | async def openai_auth_exception_handler(request: Request, exc: Exception):

FILE: python_backend/app/schemas/interpreter.py
  class InterpreterMessage (line 5) | class InterpreterMessage(BaseModel):

FILE: python_backend/app/schemas/ollama.py
  class OllamaMessages (line 7) | class OllamaMessages(BaseModel):

FILE: python_backend/main.py
  function str_to_jsonstr (line 9) | def str_to_jsonstr(data: str):
  function main (line 21) | def main():

FILE: python_backend/tools/config_tool.py
  function read_single_conf_lru_cache (line 9) | def read_single_conf_lru_cache(arg):
  function get_conf (line 21) | def get_conf(*args):
  function get_free_port (line 31) | def get_free_port():

FILE: python_backend/tools/generate_video.py
  function image_to_video_mp4 (line 14) | def image_to_video_mp4(file_path=path_img, output=video_name, height=512...
  function image_resize (line 44) | def image_resize(image_path, height, width):
  function image_crop (line 50) | def image_crop(image_path, height, width, step):
  function pillow_to_cv (line 61) | def pillow_to_cv(image_pil):
  function cv_to_pillow (line 65) | def cv_to_pillow(image_cv):
  function image_to_video_avi (line 69) | def image_to_video_avi(file_path=path_img, output=path_video_avi, height...
  function illumination_image (line 85) | def illumination_image(img):
  function flow_image (line 126) | def flow_image(img):
  function filter_image (line 149) | def filter_image(img):
  function nostalgia_image (line 177) | def nostalgia_image(img):
  function sketch_image (line 203) | def sketch_image(img):

FILE: python_backend/tools/image_effect.py
  function add_fade_effect (line 5) | def add_fade_effect(image_path, duration=1):

FILE: python_backend/tools/read_file.py
  function read (line 2) | def read(file_path):

FILE: python_backend/tools/stable_api.py
  function txt2img (line 14) | def txt2img(prompt='puppy dog',image_name='test.png'):
  function txt2img_imgname (line 35) | def txt2img_imgname(prompt='puppy dog', negative_prompt='', sampler='DPM...
  function check_and_create_path (line 57) | def check_and_create_path(path):

FILE: src/components/ClickEffect.tsx
  constant WORDS (line 5) | const WORDS = [
  type WordInstance (line 38) | interface WordInstance {

FILE: src/components/HeartBeat.tsx
  type HeartBeatProps (line 20) | interface HeartBeatProps {
  class SparkPoint (line 79) | class SparkPoint {
    method constructor (line 86) | constructor() {
    method update (line 97) | update(beatValue: number) {
  function init (line 185) | function init() {
  function render (line 197) | function render(a: number) {

FILE: src/components/Live2DDashboard.tsx
  type Live2DDashboardProps (line 46) | interface Live2DDashboardProps {

FILE: src/components/VerifyCode.tsx
  type VerifyCodeProps (line 4) | interface VerifyCodeProps {

FILE: src/components/VersionUpdateModal.tsx
  type VersionUpdateModalProps (line 13) | interface VersionUpdateModalProps {

FILE: src/components/latex/FileTree.tsx
  type FileNode (line 21) | interface FileNode {
  type FileTreeProps (line 126) | interface FileTreeProps {
  type TreeTitleProps (line 132) | interface TreeTitleProps {

FILE: src/components/latex/LatexEditor.tsx
  type LatexEditorProps (line 6) | interface LatexEditorProps {

FILE: src/components/latex/PdfPreview.tsx
  type PdfPreviewProps (line 19) | interface PdfPreviewProps {

FILE: src/components/markmap/EditNodeModal.tsx
  type EditNodeModalProps (line 7) | interface EditNodeModalProps {

FILE: src/components/markmap/EditorPanel.tsx
  type EditorPanelProps (line 8) | interface EditorPanelProps {

FILE: src/components/markmap/InfoModal.tsx
  type InfoModalProps (line 5) | interface InfoModalProps {

FILE: src/components/markmap/LandscapeMode.tsx
  type LandscapeModeProps (line 6) | interface LandscapeModeProps {

FILE: src/components/markmap/MindmapPanel.tsx
  type MindmapPanelProps (line 14) | interface MindmapPanelProps {

FILE: src/components/markmap/MobileMenu.tsx
  type MobileMenuProps (line 7) | interface MobileMenuProps {

FILE: src/components/markmap/MobileTabBar.tsx
  type MobileTabBarProps (line 5) | interface MobileTabBarProps {

FILE: src/components/markmap/PromptModal.tsx
  type PromptModalProps (line 7) | interface PromptModalProps {

FILE: src/components/video/VideoDownload.tsx
  type VideoFormat (line 10) | interface VideoFormat {
  type VideoInfo (line 21) | interface VideoInfo {
  type DownloadProgress (line 33) | interface DownloadProgress {

FILE: src/components/video/VideoParse.tsx
  type ScreenOrientation (line 12) | interface ScreenOrientation {

FILE: src/goofish/ai-tools/chat-history.tool.ts
  type ChatHistoryContext (line 8) | interface ChatHistoryContext {
  type ChatHistoryMessage (line 13) | interface ChatHistoryMessage {
  function getChatHistory (line 23) | function getChatHistory(ctx: ChatHistoryContext, limit = 10): ChatHistor...

FILE: src/goofish/ai-tools/index.ts
  constant AI_TOOLS (line 22) | const AI_TOOLS = [orderQueryToolDefinition]

FILE: src/goofish/ai-tools/order-query.tool.ts
  type OrderQueryContext (line 9) | interface OrderQueryContext {
  type OrderQueryResult (line 14) | interface OrderQueryResult {
  function queryBuyerOrders (line 32) | function queryBuyerOrders(ctx: OrderQueryContext): OrderQueryResult {

FILE: src/goofish/api/middlewares/security.middleware.ts
  constant WHITELIST_PATHS (line 14) | const WHITELIST_PATHS: string[] = [];
  function createSecurityMiddleware (line 20) | function createSecurityMiddleware() {

FILE: src/goofish/api/routes/accounts.ts
  function createAccountRoutes (line 20) | function createAccountRoutes(

FILE: src/goofish/api/routes/autoreply.ts
  function getDefaultPrompt (line 21) | function getDefaultPrompt(): string {
  function createAutoReplyRoutes (line 30) | function createAutoReplyRoutes() {

FILE: src/goofish/api/routes/autosell.ts
  function createAutoSellRoutes (line 21) | function createAutoSellRoutes() {

FILE: src/goofish/api/routes/conversations.ts
  function createConversationRoutes (line 7) | function createConversationRoutes() {

FILE: src/goofish/api/routes/dev-messages.route.ts
  constant RAW_LOG_DIR (line 18) | const RAW_LOG_DIR = join(process.cwd(), "logs", "raw");
  constant RAW_RETENTION_DAYS (line 19) | const RAW_RETENTION_DAYS = 3;
  type RawMessage (line 22) | interface RawMessage {
  constant MAX_BUFFER_SIZE (line 31) | const MAX_BUFFER_SIZE = 500;
  function decodeMessageData (line 34) | function decodeMessageData(msgData: any): any[] | null {
  function saveRawToFile (line 78) | function saveRawToFile(accountId: string, data: any, decoded: any[] | nu...
  function addRawMessage (line 107) | function addRawMessage(accountId: string, data: any) {
  function clearMessageBuffer (line 128) | function clearMessageBuffer() {
  function cleanOldRawMessages (line 133) | function cleanOldRawMessages() {
  function createDevMessageRoutes (line 169) | function createDevMessageRoutes() {

FILE: src/goofish/api/routes/goods.ts
  function createGoodsRoutes (line 7) | function createGoodsRoutes(

FILE: src/goofish/api/routes/logs.ts
  function createLogsRoutes (line 7) | function createLogsRoutes() {

FILE: src/goofish/api/routes/messages.ts
  function createMessageRoutes (line 5) | function createMessageRoutes(

FILE: src/goofish/api/routes/order.route.ts
  function createOrderRoutes (line 16) | function createOrderRoutes(

FILE: src/goofish/api/routes/status.ts
  function createStatusRoutes (line 6) | function createStatusRoutes(

FILE: src/goofish/api/routes/workflow.route.ts
  function createWorkflowRoutes (line 15) | function createWorkflowRoutes() {

FILE: src/goofish/api/routes/ws-push.route.ts
  function broadcast (line 26) | function broadcast(
  function initWSEvents (line 46) | function initWSEvents(getClientManager: () => ClientManager | null) {
  function createWSPushHandler (line 89) | function createWSPushHandler(
  function sendInitialData (line 174) | function sendInitialData(
  function getWSClientCount (line 222) | function getWSClientCount() {

FILE: src/goofish/api/server.ts
  function setClientManager (line 31) | function setClientManager(cm: ClientManager) {
  function getClientManager (line 35) | function getClientManager() {
  function createApp (line 47) | function createApp() {
  function startServer (line 129) | function startServer(port = SERVER_CONFIG.PORT) {

FILE: src/goofish/api/stores/conversation.store.ts
  class ConversationStore (line 16) | class ConversationStore {
    method addIncoming (line 17) | addIncoming(accountId: string, msg: ChatMessage) {
    method addOutgoing (line 21) | addOutgoing(
    method updateUserAvatar (line 30) | updateUserAvatar(accountId: string, chatId: string, avatar: string) {
    method getAll (line 34) | getAll(
    method get (line 41) | get(
    method markRead (line 50) | markRead(accountId: string, chatId: string) {

FILE: src/goofish/api/stores/message.store.ts
  class MessageStore (line 15) | class MessageStore {
    method add (line 16) | add(msg: ChatMessage) {
    method getRecent (line 20) | getRecent(limit = 20): StoredMessage[] {
    method getAll (line 24) | getAll(): StoredMessage[] {
    method count (line 28) | count(): number {
    method clear (line 32) | clear() {

FILE: src/goofish/core/constants.ts
  constant WS_CONFIG (line 2) | const WS_CONFIG = {
  constant API_CONFIG (line 15) | const API_CONFIG = {
  constant API_METHODS (line 21) | const API_METHODS = {
  constant PASSPORT_CONFIG (line 34) | const PASSPORT_CONFIG = {
  function buildApiUrl (line 39) | function buildApiUrl(
  constant API_ENDPOINTS (line 47) | const API_ENDPOINTS = {
  constant WS_HEADERS (line 59) | const WS_HEADERS = {
  constant LOG_CONFIG (line 71) | const LOG_CONFIG = {
  constant ENV (line 77) | const ENV = {
  constant SERVER_CONFIG (line 82) | const SERVER_CONFIG = {
  constant DB_CONFIG (line 88) | const DB_CONFIG = {

FILE: src/goofish/core/cookies.manager.ts
  class CookiesManager (line 20) | class CookiesManager {
    method getCookies (line 24) | static getCookies(accountId: string): string | null {
    method getCookiesObject (line 32) | static getCookiesObject(accountId: string): Record<string, string> {
    method getCookie (line 40) | static getCookie(accountId: string, name: string): string | undefined {
    method updateCookies (line 51) | static updateCookies(
    method handleResponseCookies (line 96) | static handleResponseCookies(accountId: string, response: Response): b...
    method getH5Token (line 114) | static getH5Token(accountId: string): string {
    method getUserId (line 122) | static getUserId(accountId: string): string | undefined {

FILE: src/goofish/core/event-emitter.ts
  constant DEBOUNCE_DELAY (line 29) | const DEBOUNCE_DELAY = 100;
  function emitOrdersUpdated (line 32) | function emitOrdersUpdated() {
  function emitAccountsUpdated (line 43) | function emitAccountsUpdated() {
  function emitConversationsUpdated (line 54) | function emitConversationsUpdated() {

FILE: src/goofish/core/logger.ts
  type LogLevel (line 6) | type LogLevel = "DEBUG" | "INFO" | "WARN" | "ERROR";
  function setLogLevel (line 18) | function setLogLevel(level: LogLevel) {
  function formatTime (line 22) | function formatTime(): string {
  function getDateStr (line 33) | function getDateStr(): string {
  function getTimestampStr (line 41) | function getTimestampStr(): string {
  function initLogFile (line 53) | function initLogFile(): string {
  function flushLogs (line 71) | function flushLogs() {
  function log (line 83) | function log(level: LogLevel, module: string, message: string) {
  function cleanOldLogs (line 101) | function cleanOldLogs(retentionDays = 7) {
  type Logger (line 131) | interface Logger {
  function createLogger (line 138) | function createLogger(module: string): Logger {

FILE: src/goofish/db/account.repository.ts
  function getEnabledAccounts (line 19) | function getEnabledAccounts(): Account[] {
  function getAllAccounts (line 29) | function getAllAccounts(): Account[] {
  function getAccount (line 39) | function getAccount(id: string): Account | null {
  function upsertAccount (line 49) | function upsertAccount(account: UpsertAccountParams): boolean {
  function updateAccountUserInfo (line 84) | function updateAccountUserInfo(
  function updateAccountCookies (line 105) | function updateAccountCookies(id: string, cookies: string): boolean {
  function updateAccountEnabled (line 122) | function updateAccountEnabled(id: string, enabled: boolean): boolean {
  function deleteAccount (line 138) | function deleteAccount(id: string): boolean {
  function updateAccountStatus (line 152) | function updateAccountStatus(
  function getAccountStatus (line 198) | function getAccountStatus(accountId: string): AccountStatus | null {

FILE: src/goofish/db/autoreply.repository.ts
  function getAutoReplyRules (line 13) | function getAutoReplyRules(): DbAutoReplyRule[] {
  function getEnabledAutoReplyRules (line 21) | function getEnabledAutoReplyRules(
  function getAutoReplyRule (line 41) | function getAutoReplyRule(id: number): DbAutoReplyRule | undefined {
  function createAutoReplyRule (line 47) | function createAutoReplyRule(rule: CreateAutoReplyRuleParams): number {
  function updateAutoReplyRule (line 66) | function updateAutoReplyRule(
  function deleteAutoReplyRule (line 99) | function deleteAutoReplyRule(id: number): boolean {
  function toggleAutoReplyRule (line 106) | function toggleAutoReplyRule(id: number): boolean {

FILE: src/goofish/db/autosell.repository.ts
  function toRule (line 20) | function toRule(row: any): AutoSellRule {
  function getAutoSellRules (line 38) | function getAutoSellRules(): AutoSellRule[] {
  function getEnabledAutoSellRules (line 45) | function getEnabledAutoSellRules(
  function getAutoSellRule (line 68) | function getAutoSellRule(id: number): AutoSellRule | undefined {
  function createAutoSellRule (line 75) | function createAutoSellRule(rule: CreateAutoSellRuleParams): number {
  function updateAutoSellRule (line 95) | function updateAutoSellRule(
  function deleteAutoSellRule (line 139) | function deleteAutoSellRule(id: number): boolean {
  function toggleAutoSellRule (line 146) | function toggleAutoSellRule(id: number): boolean {
  function toStockItem (line 157) | function toStockItem(row: DbStockItem): StockItem {
  function getStockItems (line 170) | function getStockItems(
  function getStockStats (line 183) | function getStockStats(ruleId: number): {
  function addStockItems (line 203) | function addStockItems(ruleId: number, contents: string[]): number {
  function consumeStock (line 217) | function consumeStock(
  function clearStock (line 236) | function clearStock(ruleId: number, onlyUsed = false): number {
  function toDeliveryLog (line 248) | function toDeliveryLog(row: DbDeliveryLog): DeliveryLog {
  function addDeliveryLog (line 263) | function addDeliveryLog(log: {
  function getDeliveryLogs (line 289) | function getDeliveryLogs(params: {
  function hasDelivered (line 331) | function hasDelivered(orderId: string): boolean {

FILE: src/goofish/db/connection.ts
  function closeDatabase (line 26) | function closeDatabase() {
  function getDbPath (line 31) | function getDbPath(): string {

FILE: src/goofish/db/conversation.repository.ts
  function getConversations (line 15) | function getConversations(limit = 20, offset = 0): DbConversation[] {
  function getConversationCount (line 24) | function getConversationCount(): number {
  function getConversation (line 30) | function getConversation(
  function upsertConversation (line 41) | function upsertConversation(
  function updateConversationAvatar (line 85) | function updateConversationAvatar(
  function markConversationRead (line 97) | function markConversationRead(accountId: string, chatId: string) {
  function getConversationMessages (line 106) | function getConversationMessages(
  function getConversationMessageCount (line 133) | function getConversationMessageCount(
  function getAllMessagesCount (line 144) | function getAllMessagesCount(): number {
  function addConversationMessage (line 152) | function addConversationMessage(

FILE: src/goofish/db/index.ts
  function initDatabase (line 12) | function initDatabase() {

FILE: src/goofish/db/migrations.ts
  function safeAddColumn (line 11) | function safeAddColumn(table: string, column: string, type: string) {
  function createAccountTables (line 20) | function createAccountTables() {
  function createMessageTables (line 51) | function createMessageTables() {
  function createConversationTables (line 68) | function createConversationTables() {
  function createAutoReplyTables (line 117) | function createAutoReplyTables() {
  function createUserAvatarTables (line 152) | function createUserAvatarTables() {
  function createOrderTables (line 167) | function createOrderTables() {
  function createSettingsTables (line 205) | function createSettingsTables() {
  function createChattyplayUserTables (line 216) | function createChattyplayUserTables() {
  function createAutoSellTables (line 238) | function createAutoSellTables() {
  function createWorkflowTables (line 293) | function createWorkflowTables() {
  function runMigrations (line 356) | function runMigrations() {

FILE: src/goofish/db/order.repository.ts
  function getOrders (line 11) | function getOrders(params: OrderListParams = {}): OrderRecord[] {
  function getOrderCount (line 35) | function getOrderCount(params: OrderListParams = {}): number {
  function getOrderById (line 56) | function getOrderById(orderId: string): OrderRecord | null {
  function upsertOrder (line 65) | function upsertOrder(
  function updateOrderStatus (line 117) | function updateOrderStatus(
  function deleteOrder (line 141) | function deleteOrder(orderId: string): boolean {
  function mapRowToOrder (line 152) | function mapRowToOrder(row: any): OrderRecord {

FILE: src/goofish/db/settings.repository.ts
  type DbSetting (line 7) | interface DbSetting {
  function getSetting (line 14) | function getSetting(key: string): string | null {
  function setSetting (line 21) | function setSetting(key: string, value: string): void {
  function deleteSetting (line 30) | function deleteSetting(key: string): boolean {
  function getSettings (line 37) | function getSettings(keys: string[]): Record<string, string | null> {
  constant AI_SETTINGS_KEYS (line 46) | const AI_SETTINGS_KEYS = {
  function getAISettings (line 54) | function getAISettings() {
  function saveAISettings (line 64) | function saveAISettings(settings: {

FILE: src/goofish/db/user-avatar.repository.ts
  function getUserAvatar (line 9) | function getUserAvatar(userId: string): DbUserAvatar | undefined {
  function saveUserAvatar (line 15) | function saveUserAvatar(data: SaveUserAvatarParams): boolean {
  function hasUserAvatar (line 41) | function hasUserAvatar(userId: string): boolean {

FILE: src/goofish/db/user.repository.ts
  type ChattyplayUser (line 10) | interface ChattyplayUser {
  type UserWithoutPassword (line 21) | interface UserWithoutPassword {
  class UserRepository (line 31) | class UserRepository {
    method findByUsername (line 35) | findByUsername(username: string): ChattyplayUser | undefined {
    method findByEmail (line 49) | findByEmail(email: string): ChattyplayUser | undefined {
    method findById (line 63) | findById(id: number): UserWithoutPassword | undefined {
    method create (line 77) | create(user: Omit<ChattyplayUser, 'id' | 'created_at' | 'updated_at'>)...
    method updateLastLogin (line 96) | updateLastLogin(userId: number): void {
    method updateAvatar (line 114) | updateAvatar(userId: number, avatar: string): void {
    method updateEmail (line 132) | updateEmail(userId: number, email: string): void {

FILE: src/goofish/db/workflow.repository.ts
  function getWorkflows (line 14) | function getWorkflows(): Workflow[] {
  function getWorkflowById (line 33) | function getWorkflowById(id: number): Workflow | null {
  function getDefaultWorkflow (line 53) | function getDefaultWorkflow(): Workflow | null {
  function createWorkflow (line 73) | function createWorkflow(data: {
  function updateWorkflow (line 98) | function updateWorkflow(
  function deleteWorkflow (line 138) | function deleteWorkflow(id: number): boolean {
  function createWorkflowExecution (line 150) | function createWorkflowExecution(data: {
  function getWorkflowExecution (line 175) | function getWorkflowExecution(id: number): WorkflowExecution | null {
  function getWorkflowExecutionByOrderId (line 188) | function getWorkflowExecutionByOrderId(
  function getWaitingExecutions (line 205) | function getWaitingExecutions(accountId: string): WorkflowExecution[] {
  function updateWorkflowExecution (line 218) | function updateWorkflowExecution(
  function mapExecutionRow (line 253) | function mapExecutionRow(row: any): WorkflowExecution {

FILE: src/goofish/index.ts
  function startGoofishServer (line 27) | async function startGoofishServer(port: number = 3001) {
  function fetchUserAvatarAsync (line 85) | async function fetchUserAvatarAsync(
  function fetchOrderDetailAsync (line 102) | async function fetchOrderDetailAsync(accountId: string, orderId: string) {

FILE: src/goofish/services/ai.service.ts
  function getClient (line 22) | function getClient(): OpenAI | null {
  type AIContext (line 44) | interface AIContext {
  function executeToolCall (line 55) | function executeToolCall(toolName: string, _args: any, ctx: AIContext): ...
  function generateAIReply (line 81) | async function generateAIReply(
  function testAIConnection (line 230) | async function testAIConnection(): Promise<{

FILE: src/goofish/services/auth.service.ts
  type RegisterData (line 12) | interface RegisterData {
  type LoginData (line 18) | interface LoginData {
  type AuthResponse (line 23) | interface AuthResponse {
  function register (line 33) | async function register(data: RegisterData): Promise<AuthResponse> {
  function login (line 128) | async function login(data: LoginData): Promise<AuthResponse> {
  function authenticateToken (line 187) | function authenticateToken(token: string): TokenPayload | null {
  function getUserByToken (line 194) | function getUserByToken(token: string): UserWithoutPassword | null {

FILE: src/goofish/services/autoreply.service.ts
  function checkAutoReply (line 19) | async function checkAutoReply(
  function matchAITrigger (line 113) | function matchAITrigger(rule: DbAutoReplyRule, content: string): boolean {
  function matchRule (line 137) | function matchRule(rule: DbAutoReplyRule, content: string): boolean {
  function getMatchTypeName (line 162) | function getMatchTypeName(type: string): string {

FILE: src/goofish/services/autosell.service.ts
  function fetchFromApi (line 20) | async function fetchFromApi(
  function executeDelivery (line 72) | async function executeDelivery(
  function processAutoSell (line 112) | async function processAutoSell(
  function getRuleStockStatus (line 169) | function getRuleStockStatus(ruleId: number) {

FILE: src/goofish/services/conversation.service.ts
  function addIncomingMessage (line 27) | function addIncomingMessage(accountId: string, msg: ChatMessage) {
  function addOutgoingMessage (line 60) | function addOutgoingMessage(
  function updateUserAvatar (line 102) | function updateUserAvatar(
  function getAllConversations (line 113) | function getAllConversations(
  function getConversationDetail (line 142) | function getConversationDetail(
  function markAsRead (line 188) | function markAsRead(accountId: string, chatId: string) {

FILE: src/goofish/services/goods.service.ts
  function fetchGoodsList (line 16) | async function fetchGoodsList(

FILE: src/goofish/services/message.service.ts
  constant MAX_MESSAGES (line 8) | const MAX_MESSAGES = 100;
  function addMessage (line 16) | function addMessage(msg: ChatMessage) {
  function getRecentMessages (line 28) | function getRecentMessages(limit = 20): StoredMessage[] {
  function getAllMessages (line 35) | function getAllMessages(): StoredMessage[] {
  function getMessageCount (line 42) | function getMessageCount(): number {
  function clearMessages (line 49) | function clearMessages() {

FILE: src/goofish/services/order.service.ts
  function getOrderList (line 27) | function getOrderList(params: OrderListParams) {
  function getOrder (line 39) | function getOrder(orderId: string): OrderRecord | null {
  function handleOrderMessage (line 44) | function handleOrderMessage(
  function fetchAndUpdateOrderDetail (line 73) | async function fetchAndUpdateOrderDetail(
  function triggerAutoSell (line 176) | async function triggerAutoSell(

FILE: src/goofish/services/user.service.ts
  function getCachedUserHead (line 17) | function getCachedUserHead(userId: string): UserHeadInfo | null {
  function isUserHeadCached (line 34) | function isUserHeadCached(userId: string): boolean {
  function fetchUserHead (line 41) | async function fetchUserHead(
  function fetchLoginUserId (line 137) | async function fetchLoginUserId(
  function fetchUserProfile (line 205) | async function fetchUserProfile(
  function fetchUserInfo (line 278) | async function fetchUserInfo(

FILE: src/goofish/services/workflow.service.ts
  type ExecutionContext (line 29) | interface ExecutionContext {
  function startWorkflowExecution (line 42) | async function startWorkflowExecution(
  function executeFromNode (line 103) | async function executeFromNode(
  function executeNode (line 154) | async function executeNode(
  function executeDeliveryNode (line 204) | async function executeDeliveryNode(
  function executeShipNode (line 246) | async function executeShipNode(
  function executeAutoReplyNode (line 288) | async function executeAutoReplyNode(
  function executeDelayNode (line 332) | async function executeDelayNode(
  function executeConditionNode (line 361) | async function executeConditionNode(
  function executeNotifyNode (line 373) | async function executeNotifyNode(
  function findNextNode (line 405) | function findNextNode(
  function executeDefaultFlow (line 420) | async function executeDefaultFlow(
  function handleUserReply (line 462) | async function handleUserReply(

FILE: src/goofish/types/account.types.ts
  type Account (line 8) | interface Account extends Timestamped {
  type AccountStatus (line 19) | interface AccountStatus {
  type AccountUserInfo (line 28) | interface AccountUserInfo {
  type UpsertAccountParams (line 38) | interface UpsertAccountParams {
  type UpdateAccountStatusParams (line 49) | interface UpdateAccountStatusParams {

FILE: src/goofish/types/autoreply.types.ts
  type MatchType (line 6) | type MatchType = "exact" | "contains" | "regex" | "ai";
  type AutoReplyRule (line 9) | interface AutoReplyRule {
  type DbAutoReplyRule (line 24) | interface DbAutoReplyRule {
  type CreateAutoReplyRuleParams (line 39) | interface CreateAutoReplyRuleParams {
  type UpdateAutoReplyRuleParams (line 51) | interface UpdateAutoReplyRuleParams {
  type AutoReplyResult (line 63) | interface AutoReplyResult {

FILE: src/goofish/types/autosell.types.ts
  type DeliveryType (line 6) | type DeliveryType = "fixed" | "stock" | "api";
  type TriggerOn (line 9) | type TriggerOn = "paid" | "confirmed";
  type ApiConfig (line 12) | interface ApiConfig {
  type AutoSellRule (line 21) | interface AutoSellRule {
  type DbAutoSellRule (line 39) | interface DbAutoSellRule {
  type StockItem (line 54) | interface StockItem {
  type DbStockItem (line 65) | interface DbStockItem {
  type DeliveryLog (line 76) | interface DeliveryLog {
  type DbDeliveryLog (line 89) | interface DbDeliveryLog {
  type CreateAutoSellRuleParams (line 102) | interface CreateAutoSellRuleParams {
  type UpdateAutoSellRuleParams (line 115) | interface UpdateAutoSellRuleParams {
  type DeliveryResult (line 128) | interface DeliveryResult {

FILE: src/goofish/types/common.types.ts
  type PaginationParams (line 6) | interface PaginationParams {
  type PaginatedResult (line 12) | interface PaginatedResult<T> {
  type ApiResponse (line 20) | interface ApiResponse<T = unknown> {
  type Timestamped (line 28) | interface Timestamped {

FILE: src/goofish/types/conversation.types.ts
  type Conversation (line 8) | interface Conversation {
  type DbConversation (line 23) | interface DbConversation {
  type DbConversationMessage (line 35) | interface DbConversationMessage {
  type UpsertConversationParams (line 49) | interface UpsertConversationParams {
  type AddConversationMessageParams (line 61) | interface AddConversationMessageParams {

FILE: src/goofish/types/goods.types.ts
  type GoodsItem (line 6) | interface GoodsItem {
  type GoodsListResult (line 23) | interface GoodsListResult {

FILE: src/goofish/types/message.types.ts
  type ChatMessage (line 6) | interface ChatMessage {
  type StoredMessage (line 21) | interface StoredMessage extends ChatMessage {
  type MessageDirection (line 26) | type MessageDirection = "in" | "out";
  type ConversationMessage (line 29) | interface ConversationMessage {
  type MessageCallback (line 41) | type MessageCallback = (

FILE: src/goofish/types/order.types.ts
  type OrderStatus (line 6) | enum OrderStatus {
  constant ORDER_STATUS_TEXT (line 15) | const ORDER_STATUS_TEXT: Record<number, string> = {
  type OrderRecord (line 25) | interface OrderRecord {
  type OrderDetailData (line 47) | interface OrderDetailData {
  type OrderComponent (line 66) | interface OrderComponent {
  type OrderButton (line 71) | interface OrderButton {
  type OrderListParams (line 79) | interface OrderListParams {
  type OrderListResponse (line 87) | interface OrderListResponse {

FILE: src/goofish/types/user.types.ts
  type UserHeadInfo (line 6) | interface UserHeadInfo {
  type DbUserAvatar (line 15) | interface DbUserAvatar {
  type SaveUserAvatarParams (line 26) | interface SaveUserAvatarParams {

FILE: src/goofish/types/workflow.types.ts
  type WorkflowNodeType (line 6) | type WorkflowNodeType =
  type WorkflowNode (line 16) | interface WorkflowNode {
  type WorkflowConnection (line 54) | interface WorkflowConnection {
  type WorkflowDefinition (line 62) | interface WorkflowDefinition {
  type Workflow (line 68) | interface Workflow {
  type WorkflowExecutionStatus (line 80) | type WorkflowExecutionStatus =
  type WorkflowExecution (line 88) | interface WorkflowExecution {
  constant DEFAULT_WORKFLOW (line 106) | const DEFAULT_WORKFLOW: WorkflowDefinition = {

FILE: src/goofish/utils/cookies.ts
  function parseCookies (line 2) | function parseCookies(cookiesStr: string): Record<string, string> {
  function stringifyCookies (line 16) | function stringifyCookies(cookies: Record<string, string>): string {
  function mergeCookies (line 23) | function mergeCookies(
  function parseSetCookieHeaders (line 37) | function parseSetCookieHeaders(

FILE: src/goofish/utils/crypto.ts
  function generateMid (line 3) | function generateMid(): string {
  function generateUuid (line 9) | function generateUuid(): string {
  function generateDeviceId (line 22) | function generateDeviceId(userId: string): string {
  function generateSign (line 41) | function generateSign(t: string, token: string, data: string): string {

FILE: src/goofish/utils/date.ts
  function nowLocalString (line 6) | function nowLocalString(): string {

FILE: src/goofish/utils/jwt.util.ts
  constant JWT_SECRET (line 8) | const JWT_SECRET = 'chattyplay-jwt-secret-2024'
  constant TOKEN_EXPIRY (line 9) | const TOKEN_EXPIRY = 7 * 24 * 60 * 60 * 1000 // 7天
  type TokenPayload (line 11) | interface TokenPayload {
  function generateToken (line 20) | function generateToken(payload: Omit<TokenPayload, 'exp'>): string {
  function verifyToken (line 42) | function verifyToken(token: string): TokenPayload | null {
  function base64UrlEncode (line 76) | function base64UrlEncode(str: string): string {
  function base64UrlDecode (line 86) | function base64UrlDecode(str: string): string {

FILE: src/goofish/utils/msgpack.ts
  class MessagePackDecoder (line 2) | class MessagePackDecoder {
    method constructor (line 6) | constructor(data: Buffer) {
    method readByte (line 10) | private readByte(): number {
    method readBytes (line 14) | private readBytes(count: number): Buffer {
    method readUint32 (line 20) | private readUint32(): number {
    method readString (line 26) | private readString(length: number): string {
    method decodeValue (line 30) | decodeValue(): any {
    method decodeArray (line 83) | private decodeArray(size: number): any[] {
    method decodeMap (line 87) | private decodeMap(size: number): Record<string, any> {
    method decode (line 96) | decode(): any {
  function decryptMessagePack (line 101) | function decryptMessagePack(data: string): any {

FILE: src/goofish/utils/password.util.ts
  constant SECRET_KEY (line 8) | const SECRET_KEY = 'chattyplay-secret-key-2024'
  function hashPassword (line 15) | function hashPassword(password: string): string {
  function verifyPassword (line 25) | function verifyPassword(password: string, hashedPassword: string): boole...

FILE: src/goofish/websocket/client.manager.ts
  class ClientManager (line 8) | class ClientManager {
    method constructor (line 12) | constructor(onMessage?: MessageCallback) {
    method startAll (line 17) | async startAll(): Promise<void> {
    method startClient (line 27) | async startClient(accountId: string): Promise<boolean> {
    method stopClient (line 62) | stopClient(accountId: string): boolean {
    method restartClient (line 76) | async restartClient(accountId: string): Promise<boolean> {
    method stopAll (line 83) | stopAll(): void {
    method getClient (line 93) | getClient(accountId: string): GoofishClient | undefined {
    method getStatus (line 98) | getStatus(): Array<{
    method getActiveCount (line 120) | getActiveCount(): number {

FILE: src/goofish/websocket/client.ts
  class GoofishClient (line 15) | class GoofishClient {
    method constructor (line 27) | constructor(accountId: string, onMessage?: MessageCallback) {
    method accountId (line 42) | get accountId(): string {
    method getAccountId (line 46) | getAccountId(): string {
    method isConnected (line 50) | isConnected(): boolean {
    method getUserId (line 54) | getUserId(): string {
    method sendMessage (line 58) | async sendMessage(
    method fetchOrderDetail (line 75) | async fetchOrderDetail(orderId: string): Promise<any> {
    method confirmShipment (line 150) | async confirmShipment(
    method freeShipping (line 221) | async freeShipping(
    method startHeartbeat (line 294) | private startHeartbeat() {
    method startTokenRefresh (line 308) | private startTokenRefresh() {
    method initConnection (line 329) | private async initConnection() {
    method sendAck (line 382) | private sendAck(headers: any) {
    method connect (line 391) | async connect(): Promise<boolean> {
    method cleanup (line 471) | private cleanup() {
    method disconnect (line 482) | disconnect() {
    method run (line 493) | async run() {

FILE: src/goofish/websocket/message.parser.ts
  constant SYSTEM_MESSAGES (line 8) | const SYSTEM_MESSAGES = [
  constant ORDER_STATUS_MESSAGES (line 19) | const ORDER_STATUS_MESSAGES = [
  function isSystemMessage (line 34) | function isSystemMessage(content: string): boolean {
  function isOrderStatusMessage (line 38) | function isOrderStatusMessage(content: string): boolean {
  function decryptSyncData (line 42) | function decryptSyncData(data: string): any | null {
  function extractChatMessage (line 75) | function extractChatMessage(

FILE: src/goofish/websocket/message.receiver.ts
  type MessageReceiverContext (line 14) | interface MessageReceiverContext {
  function handleSyncMessage (line 29) | async function handleSyncMessage(
  function processWebSocketMessage (line 134) | async function processWebSocketMessage(

FILE: src/goofish/websocket/message.sender.ts
  type SendMessageOptions (line 8) | interface SendMessageOptions {
  function sendMessage (line 20) | async function sendMessage(

FILE: src/goofish/websocket/token.ts
  constant SESSION_EXPIRED_ERRORS (line 9) | const SESSION_EXPIRED_ERRORS = ["FAIL_SYS_SESSION_EXPIRED", "SESSION_EXP...
  class TokenManager (line 11) | class TokenManager {
    method constructor (line 17) | constructor(accountId: string, deviceId: string) {
    method getToken (line 22) | getToken(): string | null {
    method isExpired (line 26) | isExpired(): boolean {
    method hasLogin (line 35) | private async hasLogin(retryCount = 0): Promise<boolean> {
    method isSessionExpiredError (line 115) | private isSessionExpiredError(error: string): boolean {
    method doTokenRequest (line 120) | private async doTokenRequest(): Promise<{
    method refresh (line 190) | async refresh(): Promise<string | null> {

FILE: src/hooks/goofish/useWebSocket.ts
  type UseWebSocketOptions (line 4) | interface UseWebSocketOptions {
  function useGoofishWebSocket (line 10) | function useGoofishWebSocket(options: UseWebSocketOptions = {}) {

FILE: src/main.tsx
  type Window (line 33) | interface Window {
  type ErrorBoundaryState (line 41) | interface ErrorBoundaryState {
  type ErrorBoundaryProps (line 45) | interface ErrorBoundaryProps {
  class ErrorBoundary (line 49) | class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryS...
    method constructor (line 50) | constructor(props: ErrorBoundaryProps) {
    method componentDidCatch (line 55) | componentDidCatch(error: Error, errorInfo: any) {
    method render (line 67) | render() {

FILE: src/pages/Cartoon.tsx
  type RankType (line 126) | interface RankType {
  type Topic (line 132) | interface Topic {
  type CartoonData (line 142) | interface CartoonData {

FILE: src/pages/CartoonChapter.tsx
  type ComicInfo (line 136) | interface ComicInfo {

FILE: src/pages/CartoonDetail.tsx
  type TopicInfo (line 99) | interface TopicInfo {

FILE: src/pages/GPT.tsx
  type ChatMessage (line 31) | interface ChatMessage {
  type CaptchaPromise (line 38) | interface CaptchaPromise {

FILE: src/pages/Gold.tsx
  type PriceData (line 13) | interface PriceData {
  type KlineData (line 34) | interface KlineData {

FILE: src/pages/Login.tsx
  constant TURNSTILE_SITE_KEY (line 23) | const TURNSTILE_SITE_KEY = import.meta.env.VITE_TURNSTILE_SITE_KEY || ''
  method validator (line 833) | validator(_, value) {

FILE: src/pages/Music.tsx
  type SearchResult (line 404) | interface SearchResult {
  type Comment (line 413) | interface Comment {

FILE: src/pages/PaperListPage.tsx
  type AuthorData (line 32) | interface AuthorData {
  type InferenceProvider (line 47) | interface InferenceProvider {
  type ModelData (line 65) | interface ModelData {
  type DatasetData (line 82) | interface DatasetData {
  type SpaceData (line 102) | interface SpaceData {
  type RepoData (line 153) | interface RepoData {
  type PaperDetail (line 160) | interface PaperDetail {

FILE: src/pages/Trans.tsx
  constant BAIDU_APP_ID (line 66) | const BAIDU_APP_ID = import.meta.env.VITE_BAIDU_APP_ID || ''
  constant BAIDU_SECRET_KEY (line 67) | const BAIDU_SECRET_KEY = import.meta.env.VITE_BAIDU_SECRET_KEY || ''

FILE: src/pages/goofish/AutoReply/index.tsx
  constant MATCH_TYPES (line 31) | const MATCH_TYPES = [

FILE: src/pages/goofish/AutoSell/index.tsx
  constant DELIVERY_TYPES (line 32) | const DELIVERY_TYPES = [

FILE: src/pages/goofish/Conversations/index.tsx
  type MessageItem (line 28) | interface MessageItem {

FILE: src/pages/goofish/Goods/index.tsx
  type GoodsItem (line 27) | interface GoodsItem {

FILE: src/pages/goofish/Logs/index.tsx
  constant LEVEL_CONFIG (line 29) | const LEVEL_CONFIG: Record<string, { color: string; icon: any }> = {

FILE: src/pages/goofish/Orders/index.tsx
  type OrderStatus (line 34) | enum OrderStatus {
  constant STATUS_CONFIG (line 43) | const STATUS_CONFIG: Record<number, { text: string; color: string }> = {

FILE: src/pages/goofish/Workflow/index.tsx
  constant NODE_TYPES (line 30) | const NODE_TYPES = [

FILE: src/sdk/analytics/examples.tsx
  function BasicExample (line 11) | function BasicExample() {
  function PageTrackingExample (line 84) | function PageTrackingExample() {
  function ClickTrackingExample (line 102) | function ClickTrackingExample() {
  function VisibilityTrackingExample (line 138) | function VisibilityTrackingExample() {
  function ScrollTrackingExample (line 177) | function ScrollTrackingExample() {
  function ComprehensiveExample (line 200) | function ComprehensiveExample() {
  function AnalyticsIntegrationExample (line 370) | function AnalyticsIntegrationExample() {

FILE: src/sdk/analytics/hooks.ts
  function useAnalytics (line 11) | function useAnalytics() {
  function usePageTracking (line 58) | function usePageTracking(enabled: boolean = true) {
  function useEventTracking (line 70) | function useEventTracking(eventName: string, properties?: Record<string,...
  function useClickTracking (line 81) | function useClickTracking(elementRef: React.RefObject<HTMLElement>, even...
  function useVisibilityTracking (line 102) | function useVisibilityTracking(elementRef: React.RefObject<HTMLElement>,...
  function useFormTracking (line 136) | function useFormTracking(formName: string, onSuccess?: (data: any) => vo...
  function useScrollTracking (line 163) | function useScrollTracking(threshold: number = 50, eventName: string = '...
  function useDurationTracking (line 192) | function useDurationTracking(pageName: string, reportInterval: number = ...

FILE: src/sdk/analytics/index.ts
  type AnalyticsConfig (line 7) | interface AnalyticsConfig {
  type EventData (line 29) | interface EventData {
  type DeviceInfo (line 49) | interface DeviceInfo {
  type ReportData (line 59) | interface ReportData extends EventData {
  class AnalyticsSDK (line 65) | class AnalyticsSDK {
    method constructor (line 72) | constructor(config: AnalyticsConfig) {
    method init (line 90) | init(): void {
    method track (line 117) | track(event: string, properties: Record<string, any> = {}): void {
    method trackPageView (line 144) | trackPageView(): void {
    method trackAction (line 155) | trackAction(action: string, target: string, properties: Record<string,...
    method trackClick (line 166) | trackClick(buttonName: string, properties: Record<string, any> = {}): ...
    method trackSubmit (line 176) | trackSubmit(formName: string, properties: Record<string, any> = {}): v...
    method trackError (line 186) | trackError(error: Error | string, context: Record<string, any> = {}): ...
    method setUserId (line 200) | setUserId(userId: string): void {
    method setUserProperties (line 208) | setUserProperties(properties: Record<string, any>): void {
    method generateSessionId (line 215) | private generateSessionId(): string {
    method getDeviceInfo (line 222) | private getDeviceInfo(): DeviceInfo {
    method generateFingerprint (line 239) | private generateFingerprint(): string {
    method addToBatch (line 263) | private addToBatch(eventData: EventData): void {
    method startBatchTimer (line 276) | private startBatchTimer(): void {
    method flushBatch (line 289) | private flushBatch(): void {
    method sendEvent (line 306) | private sendEvent(eventData: EventData): void {
    method sendBatch (line 320) | private sendBatch(events: EventData[]): void {
    method sendViaGIF (line 334) | private sendViaGIF(data: any, isBatch = false): void {
    method btoa (line 362) | private btoa(str: string): string {
    method trackPageChanges (line 374) | private trackPageChanges(): void {
    method setupErrorTracking (line 403) | private setupErrorTracking(): void {
    method setupUnloadTracking (line 435) | private setupUnloadTracking(): void {
    method log (line 455) | private log(...args: any[]): void {
    method warn (line 464) | private warn(...args: any[]): void {
    method destroy (line 473) | destroy(): void {
  function initAnalytics (line 495) | function initAnalytics(config: AnalyticsConfig): AnalyticsSDK {
  function getAnalytics (line 503) | function getAnalytics(): AnalyticsSDK | null {

FILE: src/sdk/analytics/types.d.ts
  type Window (line 8) | interface Window {

FILE: src/sdk/fingerprint/collector.ts
  class FingerprintCollector (line 8) | class FingerprintCollector {
    method collectBrowserInfo (line 12) | static collectBrowserInfo(): BrowserInfo {
    method collectScreenInfo (line 29) | static collectScreenInfo(): ScreenInfo {
    method collectCanvasInfo (line 46) | static collectCanvasInfo(): CanvasInfo {
    method getWebGLFingerprint (line 104) | private static getWebGLFingerprint(gl: WebGLRenderingContext): string {
    method collectAudioInfo (line 134) | static collectAudioInfo(): AudioInfo {
    method collectTimeZoneInfo (line 181) | static collectTimeZoneInfo(): TimeZoneInfo {
    method collectFeatureInfo (line 192) | static collectFeatureInfo(): FeatureInfo {
    method testLocalStorage (line 216) | private static testLocalStorage(): boolean {
    method testSessionStorage (line 230) | private static testSessionStorage(): boolean {
    method testIndexedDB (line 244) | private static testIndexedDB(): boolean {
    method collectAll (line 255) | static collectAll(options: {

FILE: src/sdk/fingerprint/examples.tsx
  function BasicFingerprintExample (line 19) | function BasicFingerprintExample() {
  function VisitStatsExample (line 45) | function VisitStatsExample() {
  function FullFingerprintExample (line 65) | function FullFingerprintExample() {
  function FingerprintCompareExample (line 205) | function FingerprintCompareExample() {
  function AdvancedFingerprintExample (line 249) | function AdvancedFingerprintExample() {
  function SimpleVisitorIdExample (line 313) | function SimpleVisitorIdExample() {
  function FingerprintDashboard (line 328) | function FingerprintDashboard() {

FILE: src/sdk/fingerprint/hash.ts
  class FingerprintHasher (line 8) | class FingerprintHasher {
    method generateHash (line 12) | static async generateHash(
    method normalizeData (line 40) | private static normalizeData(data: any): string {
    method sha256 (line 61) | private static async sha256(data: string): Promise<string> {
    method sha384 (line 71) | private static async sha384(data: string): Promise<string> {
    method sha512 (line 81) | private static async sha512(data: string): Promise<string> {
    method md5 (line 91) | private static md5(data: string): string {
    method simpleHash (line 257) | private static simpleHash(data: string): string {
    method generateVisitorId (line 273) | static generateVisitorId(): string {
    method calculateConfidence (line 284) | static calculateConfidence(data: FingerprintData): number {

FILE: src/sdk/fingerprint/hooks.ts
  type UseFingerprintOptions (line 10) | interface UseFingerprintOptions extends FingerprintOptions {
  type UseFingerprintReturn (line 28) | interface UseFingerprintReturn {
  function useFingerprint (line 114) | function useFingerprint(options: UseFingerprintOptions = {}): UseFingerp...
  function useVisitorId (line 253) | function useVisitorId(): string | null {
  function useFingerprintCompare (line 293) | function useFingerprintCompare() {
  function useVisitStats (line 332) | function useVisitStats() {

FILE: src/sdk/fingerprint/index.ts
  class FingerprintSDK (line 24) | class FingerprintSDK {
    method constructor (line 29) | constructor(options: FingerprintOptions = {}) {
    method getFingerprint (line 48) | async getFingerprint(): Promise<FingerprintResult> {
    method generateFingerprint (line 81) | async generateFingerprint(): Promise<FingerprintResult> {
    method validateFingerprint (line 117) | async validateFingerprint(expectedHash: string): Promise<boolean> {
    method getVisitorId (line 136) | getVisitorId(): string | null {
    method getHash (line 143) | getHash(): string | null {
    method getVisitStats (line 150) | getVisitStats(): { firstSeen: number; lastSeen: number; visitCount: nu...
    method clearFingerprint (line 157) | clearFingerprint(): boolean {
    method exportFingerprint (line 165) | exportFingerprint(): string {
    method importFingerprint (line 172) | importFingerprint(exportedData: string): boolean {
    method getCurrentFingerprint (line 179) | getCurrentFingerprint(): FingerprintResult | null {
    method regenerateFingerprint (line 186) | async regenerateFingerprint(): Promise<FingerprintResult> {
    method isFirstVisit (line 194) | isFirstVisit(): boolean {
    method getFingerprintDetails (line 202) | getFingerprintDetails(): Partial<FingerprintData> | null {
    method compareFingerprints (line 220) | compareFingerprints(otherHash: string): number {
  function getFingerprint (line 259) | async function getFingerprint(): Promise<FingerprintResult> {
  function getVisitorId (line 263) | function getVisitorId(): string | null {
  function getHash (line 267) | function getHash(): string | null {
  function clearFingerprint (line 271) | function clearFingerprint(): boolean {

FILE: src/sdk/fingerprint/storage.ts
  class FingerprintStorage (line 8) | class FingerprintStorage {
    method constructor (line 11) | constructor(storageKey: string = 'fingerprint_data') {
    method save (line 18) | save(data: StorageData): boolean {
    method load (line 32) | load(): StorageData | null {
    method updateVisit (line 48) | updateVisit(): StorageData | null {
    method clear (line 62) | clear(): boolean {
    method exists (line 75) | exists(): boolean {
    method getVisitorId (line 82) | getVisitorId(): string | null {
    method getHash (line 90) | getHash(): string | null {
    method getVisitStats (line 98) | getVisitStats(): { firstSeen: number; lastSeen: number; visitCount: nu...
    method setSessionData (line 112) | setSessionData(key: string, value: string): boolean {
    method getSessionData (line 125) | getSessionData(key: string): string | null {
    method clearSessionData (line 137) | clearSessionData(key?: string): boolean {
    method export (line 157) | export(): string {
    method import (line 167) | import(exportedData: string): boolean {

FILE: src/sdk/fingerprint/types.ts
  type BrowserInfo (line 5) | interface BrowserInfo {
  type ScreenInfo (line 17) | interface ScreenInfo {
  type CanvasInfo (line 28) | interface CanvasInfo {
  type AudioInfo (line 35) | interface AudioInfo {
  type TimeZoneInfo (line 39) | interface TimeZoneInfo {
  type FeatureInfo (line 45) | interface FeatureInfo {
  type FingerprintData (line 55) | interface FingerprintData {
  type FingerprintOptions (line 65) | interface FingerprintOptions {
  type FingerprintResult (line 76) | interface FingerprintResult {
  type StorageData (line 83) | interface StorageData {

FILE: src/services/goofish/index.ts
  constant API_BASE (line 8) | const API_BASE = '/api'

FILE: src/services/latexApi.ts
  type CompileOptions (line 6) | interface CompileOptions {
  type CompileResult (line 16) | interface CompileResult {
  function compileLatex (line 29) | async function compileLatex({ code, compiler = 'xelatex', timeout = 6000...
  function checkApiHealth (line 99) | async function checkApiHealth(): Promise<boolean> {

FILE: src/services/server.ts
  function initGoofish (line 37) | async function initGoofish() {
  function fetchUserAvatarAsync (line 76) | async function fetchUserAvatarAsync(accountId: string, chatId: string, u...
  function fetchOrderDetailAsync (line 88) | async function fetchOrderDetailAsync(accountId: string, orderId: string) {
  function getLocalIP (line 102) | function getLocalIP(): string {
  method start (line 616) | async start(controller) {
  method start (line 693) | async start(controller) {
  constant PROXY_CONFIG (line 732) | const PROXY_CONFIG: Record<string, {
  function matchProxyTarget (line 776) | function matchProxyTarget(path: string): { prefix: string; config: typeo...
  function rewritePath (line 795) | function rewritePath(path: string, prefix: string, replacement: string):...
  constant CACHE_TTL (line 801) | const CACHE_TTL = 5 * 60 * 1000 // 5分钟

FILE: src/services/turnstile.ts
  constant TURNSTILE_SECRET_KEY (line 5) | const TURNSTILE_SECRET_KEY = process.env.TURNSTILE_SECRET_KEY || ''
  constant TURNSTILE_VERIFY_URL (line 6) | const TURNSTILE_VERIFY_URL = 'https://challenges.cloudflare.com/turnstil...
  type TurnstileVerifyResult (line 8) | interface TurnstileVerifyResult {
  function verifyTurnstileToken (line 18) | async function verifyTurnstileToken(token: string): Promise<{

FILE: src/types/goofish/index.ts
  type Account (line 6) | interface Account {
  type ClientStatus (line 18) | interface ClientStatus {
  type StatusResponse (line 24) | interface StatusResponse {
  type Message (line 31) | interface Message {
  type Conversation (line 43) | interface Conversation {
  type Order (line 57) | interface Order {
  type AutoReplyRule (line 79) | interface AutoReplyRule {
  type AutoSellRule (line 94) | interface AutoSellRule {
  type Workflow (line 110) | interface Workflow {
  type WorkflowDefinition (line 121) | interface WorkflowDefinition {
  type WorkflowNodeType (line 126) | type WorkflowNodeType =
  type WorkflowNode (line 135) | interface WorkflowNode {
  type WorkflowEdge (line 142) | interface WorkflowEdge {
  type LogEntry (line 150) | interface LogEntry {

FILE: src/types/turnstile.d.ts
  type TurnstileObject (line 5) | interface TurnstileObject {
  type TurnstileConfig (line 12) | interface TurnstileConfig {
  type Window (line 24) | interface Window {

FILE: src/utils/markmap.ts
  type Window (line 2) | interface Window {
  type AiResult (line 12) | interface AiResult {
  type NodeContext (line 17) | interface NodeContext {
  constant LAST_SUCCESSFUL_MODEL_KEY (line 22) | const LAST_SUCCESSFUL_MODEL_KEY = 'ai-mindmap-last-successful-model'

FILE: src/utils/paper.ts
  type AuthorData (line 3) | interface AuthorData {

FILE: src/utils/token.ts
  constant TOKEN_KEY (line 6) | const TOKEN_KEY = 'chattyplay-token'

FILE: src/utils/versionChecker.ts
  type VersionInfo (line 6) | interface VersionInfo {
  constant VERSION_STORAGE_KEY (line 11) | const VERSION_STORAGE_KEY = 'app_version'
  constant VERSION_CHECK_FLAG_KEY (line 12) | const VERSION_CHECK_FLAG_KEY = 'version_update_checked'

FILE: src/vite-env.d.ts
  type ImportMetaEnv (line 3) | interface ImportMetaEnv {
  type ImportMeta (line 10) | interface ImportMeta {

FILE: vite.config.ts
  function honoServerPlugin (line 9) | function honoServerPlugin() {
  function copyHtaccessPlugin (line 30) | function copyHtaccessPlugin() {
Condensed preview — 285 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,705K chars).
[
  {
    "path": ".browserslistrc",
    "chars": 30,
    "preview": "> 1%\nlast 2 versions\nnot dead\n"
  },
  {
    "path": ".dockerignore",
    "chars": 281,
    "preview": "# 依赖\nnode_modules\nnpm-debug.log\nyarn-error.log\nyarn-debug.log\npackage-lock.json\n\n# 构建产物\ndist\nbuild\n\n# 环境变量\n.env.local\n.e"
  },
  {
    "path": ".gitignore",
    "chars": 355,
    "preview": ".DS_Store\nnode_modules\n/dist\n\n# 闲鱼日志文件\n/logs\n# 闲鱼数据库文件\n/data\n\n\n# local env files - ignore all .env files except .env.exa"
  },
  {
    "path": "Dockerfile",
    "chars": 531,
    "preview": "# 多阶段构建 - 构建阶段\nFROM node:20-alpine AS builder\n\n# 设置工作目录\nWORKDIR /app\n\n# 安装 yarn\nRUN corepack enable && corepack prepare "
  },
  {
    "path": "LICENSE",
    "chars": 11357,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "README.md",
    "chars": 19166,
    "preview": "<h1 align=\"center\">\n <p>ChattyPlay-Agent</p>\n <a href=\"https://trendshift.io/repositories/20110\" target=\"_blank\"><img sr"
  },
  {
    "path": "api/index.ts",
    "chars": 24366,
    "preview": "import { Hono } from 'hono'\nimport { cors } from 'hono/cors'\nimport CryptoJS from 'crypto-js'\n\nconst app = new Hono()\n\n/"
  },
  {
    "path": "deploy.sh",
    "chars": 1594,
    "preview": "#!/bin/bash\n\n# 颜色定义\nRED='\\033[0;31m'\nGREEN='\\033[0;32m'\nYELLOW='\\033[1;33m'\nNC='\\033[0m' # No Color\n\n# 项目名称\nPROJECT_NAME"
  },
  {
    "path": "docker-compose.yml",
    "chars": 495,
    "preview": "version: '3.8'\n\nservices:\n  # 前端应用\n  web:\n    build:\n      context: .\n      dockerfile: Dockerfile\n    container_name: c"
  },
  {
    "path": "email.config.d.ts",
    "chars": 175,
    "preview": "export interface EmailConfig {\n  PUBLIC_KEY: string\n  SERVICE_ID: string\n  TEMPLATE_ID: string\n  fromName: string\n  from"
  },
  {
    "path": "email.config.js",
    "chars": 1058,
    "preview": "/**\n * EmailJS 配置文件\n *\n * 使用说明:\n * 1. 访问 https://www.emailjs.com/ 注册账号\n * 2. 添加邮件服务(Email Service),支持:\n *    - Gmail\n * "
  },
  {
    "path": "index.html",
    "chars": 6059,
    "preview": "<!doctype html>\n<html lang=\"zh-CN\">\n<head>\n  <meta charset=\"UTF-8\" />\n  <link rel=\"icon\" type=\"image/svg+xml\" href=\"/pub"
  },
  {
    "path": "jsconfig.json",
    "chars": 279,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"module\": \"esnext\",\n    \"baseUrl\": \"./\",\n    \"moduleResolution\": \"node"
  },
  {
    "path": "package.json",
    "chars": 2701,
    "preview": "{\n  \"name\": \"chattyplay-agent\",\n  \"private\": true,\n  \"author\": {\n    \"name\": \"P1Kaj1uu\",\n    \"email\": \"891523233@qq.com\""
  },
  {
    "path": "postcss.config.js",
    "chars": 80,
    "preview": "export default {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n}\n"
  },
  {
    "path": "public/.htaccess",
    "chars": 1135,
    "preview": "# Apache 配置文件 - 用于生产环境部署\n# 支持单页应用(SPA)路由和 API 代理\n\n# 启用代理模块\n<IfModule mod_proxy.c>\n  ProxyRequests Off\n  ProxyPreserveHos"
  },
  {
    "path": "public/index.html",
    "chars": 1651,
    "preview": "<!DOCTYPE html>\n<html lang=\"\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=ed"
  },
  {
    "path": "public/layui/css/layui.css",
    "chars": 60772,
    "preview": "/** layui-v2.3.0 MIT License By https://www.layui.com */\n .layui-inline,img{display:inline-block;vertical-align:middle}."
  },
  {
    "path": "public/layui/css/layui.mobile.css",
    "chars": 9885,
    "preview": "/** layui-v2.3.0 MIT License By https://www.layui.com */\n blockquote,body,button,dd,div,dl,dt,form,h1,h2,h3,h4,h5,h6,inp"
  },
  {
    "path": "public/layui/css/modules/code.css",
    "chars": 1063,
    "preview": "/** layui-v2.3.0 MIT License By https://www.layui.com */\n html #layuicss-skincodecss{display:none;position:absolute;widt"
  },
  {
    "path": "public/layui/css/modules/laydate/default/laydate.css",
    "chars": 7537,
    "preview": "/** layui-v2.3.0 MIT License By https://www.layui.com */\n .laydate-set-ym,.layui-laydate,.layui-laydate *,.layui-laydate"
  },
  {
    "path": "public/layui/css/modules/layer/default/layer.css",
    "chars": 14425,
    "preview": "/** layui-v2.3.0 MIT License By https://www.layui.com */\n .layui-layer-imgbar,.layui-layer-imgtit a,.layui-layer-tab .la"
  },
  {
    "path": "public/layui/lay/modules/carousel.js",
    "chars": 3839,
    "preview": "/** layui-v2.3.0 MIT License By https://www.layui.com */\n ;layui.define(\"jquery\",function(e){\"use strict\";var i=layui.$,"
  },
  {
    "path": "public/layui/lay/modules/code.js",
    "chars": 1177,
    "preview": "/** layui-v2.3.0 MIT License By https://www.layui.com */\n ;layui.define(\"jquery\",function(e){\"use strict\";var a=layui.$,"
  },
  {
    "path": "public/layui/lay/modules/element.js",
    "chars": 7260,
    "preview": "/** layui-v2.3.0 MIT License By https://www.layui.com */\n ;layui.define(\"jquery\",function(t){\"use strict\";var a=layui.$,"
  },
  {
    "path": "public/layui/lay/modules/flow.js",
    "chars": 1996,
    "preview": "/** layui-v2.3.0 MIT License By https://www.layui.com */\n ;layui.define(\"jquery\",function(e){\"use strict\";var l=layui.$,"
  },
  {
    "path": "public/layui/lay/modules/form.js",
    "chars": 8718,
    "preview": "/** layui-v2.3.0 MIT License By https://www.layui.com */\n ;layui.define(\"layer\",function(e){\"use strict\";var i=layui.$,t"
  },
  {
    "path": "public/layui/lay/modules/jquery.js",
    "chars": 97648,
    "preview": "/** layui-v2.3.0 MIT License By https://www.layui.com */\n ;!function(e,t){\"object\"==typeof module&&\"object\"==typeof modu"
  },
  {
    "path": "public/layui/lay/modules/laydate.js",
    "chars": 27043,
    "preview": "/** layui-v2.3.0 MIT License By https://www.layui.com */\n ;!function(){\"use strict\";var e=window.layui&&layui.define,t={"
  },
  {
    "path": "public/layui/lay/modules/layedit.js",
    "chars": 12221,
    "preview": "/** layui-v2.3.0 MIT License By https://www.layui.com */\n ;layui.define([\"layer\",\"form\"],function(t){\"use strict\";var e="
  },
  {
    "path": "public/layui/lay/modules/layer.js",
    "chars": 22063,
    "preview": "/** layui-v2.3.0 MIT License By https://www.layui.com */\n ;!function(e,t){\"use strict\";var i,n,a=e.layui&&layui.define,o"
  },
  {
    "path": "public/layui/lay/modules/laypage.js",
    "chars": 4464,
    "preview": "/** layui-v2.3.0 MIT License By https://www.layui.com */\n ;layui.define(function(e){\"use strict\";var a=document,t=\"getEl"
  },
  {
    "path": "public/layui/lay/modules/laytpl.js",
    "chars": 1834,
    "preview": "/** layui-v2.3.0 MIT License By https://www.layui.com */\n ;layui.define(function(e){\"use strict\";var r={open:\"{{\",close:"
  },
  {
    "path": "public/layui/lay/modules/mobile.js",
    "chars": 33700,
    "preview": "/** layui-v2.3.0 MIT License By https://www.layui.com */\n ;layui.define(function(i){i(\"layui.mobile\",layui.v)});layui.de"
  },
  {
    "path": "public/layui/lay/modules/rate.js",
    "chars": 2749,
    "preview": "/** layui-v2.3.0 MIT License By https://www.layui.com */\n ;layui.define(\"jquery\",function(e){\"use strict\";var a=layui.jq"
  },
  {
    "path": "public/layui/lay/modules/table.js",
    "chars": 20778,
    "preview": "/** layui-v2.3.0 MIT License By https://www.layui.com */\n ;layui.define([\"laytpl\",\"laypage\",\"layer\",\"form\"],function(e){"
  },
  {
    "path": "public/layui/lay/modules/tree.js",
    "chars": 3042,
    "preview": "/** layui-v2.3.0 MIT License By https://www.layui.com */\n ;layui.define(\"jquery\",function(e){\"use strict\";var o=layui.$,"
  },
  {
    "path": "public/layui/lay/modules/upload.js",
    "chars": 6757,
    "preview": "/** layui-v2.3.0 MIT License By https://www.layui.com */\n ;layui.define(\"layer\",function(e){\"use strict\";var i=layui.$,t"
  },
  {
    "path": "public/layui/lay/modules/util.js",
    "chars": 2588,
    "preview": "/** layui-v2.3.0 MIT License By https://www.layui.com */\n ;layui.define(\"jquery\",function(e){\"use strict\";var t=layui.$,"
  },
  {
    "path": "public/layui/layui.all.js",
    "chars": 229946,
    "preview": "/** layui-v2.3.0 MIT License By https://www.layui.com */\n ;!function(e){\"use strict\";var t=document,n={modules:{},status"
  },
  {
    "path": "public/layui/layui.js",
    "chars": 6592,
    "preview": "/** layui-v2.3.0 MIT License By https://www.layui.com */\n ;!function(e){\"use strict\";var t=document,n={modules:{},status"
  },
  {
    "path": "public/model/heartBeat.obj",
    "chars": 774038,
    "preview": "# 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware\n# File Created: 05.12.2011 16:42:58\n\nmtllib 12190_Heart_v1_L3"
  },
  {
    "path": "public/three/MeshSurfaceSampler.js",
    "chars": 4497,
    "preview": "( function () {\n\n\t/**\n * Utility class for sampling weighted random points on the surface of a mesh.\n *\n * Building the "
  },
  {
    "path": "public/three/OBJLoader.js",
    "chars": 21560,
    "preview": "( function () {\n\n\tconst _object_pattern = /^[og]\\s*(.+)?/; // mtllib file_reference\n\n\tconst _material_library_pattern = "
  },
  {
    "path": "public/three/TrackballControls.js",
    "chars": 17825,
    "preview": "( function () {\n\n\tconst _changeEvent = {\n\t\ttype: 'change'\n\t};\n\tconst _startEvent = {\n\t\ttype: 'start'\n\t};\n\tconst _endEven"
  },
  {
    "path": "public/three/simplex-noise.js",
    "chars": 17079,
    "preview": "/*\n * A fast javascript implementation of simplex noise by Jonas Wagner\n\nBased on a speed-improved simplex noise algorit"
  },
  {
    "path": "python_backend/app/api/routers.py",
    "chars": 402,
    "preview": "# -*- coding: utf-8 -*-\nfrom fastapi import APIRouter\nfrom app.api.v1.interpreter import router as interpreter_router\nfr"
  },
  {
    "path": "python_backend/app/api/v1/interpreter.py",
    "chars": 1096,
    "preview": "# -*- coding: utf-8 -*-\n\nfrom fastapi import APIRouter\nfrom fastapi.responses import StreamingResponse\nfrom app.common.o"
  },
  {
    "path": "python_backend/app/api/v1/ollama.py",
    "chars": 1704,
    "preview": "# -*- coding: utf-8 -*-\nfrom typing import Annotated\nimport msgspec.json\nfrom fastapi import APIRouter, Query\nfrom ollam"
  },
  {
    "path": "python_backend/app/common/log.py",
    "chars": 704,
    "preview": "# -*- coding: utf-8 -*-\nfrom __future__ import annotations\nimport os\nimport loguru\nfrom loguru import logger\nfrom app.co"
  },
  {
    "path": "python_backend/app/common/open_interpreter.py",
    "chars": 795,
    "preview": "# -*- coding: utf-8 -*-\nfrom interpreter.core.core import OpenInterpreter as _OpenInterpreter\nfrom app.core.conf import "
  },
  {
    "path": "python_backend/app/common/response.py",
    "chars": 176,
    "preview": "# -*- coding: utf-8 -*-\nfrom typing import Any\nfrom pydantic import BaseModel\n\nclass ResponseModel(BaseModel):\n    code:"
  },
  {
    "path": "python_backend/app/core/conf.py",
    "chars": 1145,
    "preview": "# -*- coding: utf-8 -*-\nfrom functools import lru_cache\n\nfrom pydantic_settings import BaseSettings, SettingsConfigDict\n"
  },
  {
    "path": "python_backend/app/core/path_conf.py",
    "chars": 175,
    "preview": "# -*- coding: utf-8 -*-\nimport os\n\nfrom pathlib import Path\n\nBasePath = Path(__file__).resolve().parent.parent.parent\n\n#"
  },
  {
    "path": "python_backend/app/main.py",
    "chars": 1697,
    "preview": "# -*- coding: utf-8 -*-\nimport uvicorn\nfrom fastapi import FastAPI, Request\nfrom starlette.responses import JSONResponse"
  },
  {
    "path": "python_backend/app/requirements.txt",
    "chars": 950,
    "preview": "# 核心依赖\nfastapi==0.110.0\nuvicorn==0.27.1\npydantic==2.4.2\npydantic-settings==2.2.1\npython-dotenv==1.0.0\npython-multipart=="
  },
  {
    "path": "python_backend/app/schemas/interpreter.py",
    "chars": 115,
    "preview": "# -*- coding: utf-8 -*-\nfrom pydantic import BaseModel\n\n\nclass InterpreterMessage(BaseModel):\n    message: str\n    "
  },
  {
    "path": "python_backend/app/schemas/ollama.py",
    "chars": 220,
    "preview": "# -*- coding: utf-8 -*-\nfrom typing import Literal\n\nfrom pydantic import BaseModel\n\n\nclass OllamaMessages(BaseModel):\n  "
  },
  {
    "path": "python_backend/config/config.py",
    "chars": 211,
    "preview": "# -*- coding: utf-8 -*-\n#文心模型类型\nAPI_TYPE = \"aistudio\"\n#文心模型token 地址地址https://aistudio.baidu.com/index/accessToken\nACCESS"
  },
  {
    "path": "python_backend/config/config_private.py",
    "chars": 211,
    "preview": "# -*- coding: utf-8 -*-\n#文心模型类型\nAPI_TYPE = \"aistudio\"\n#文心模型token 申请地址https://aistudio.baidu.com/index/accessToken\nACCESS"
  },
  {
    "path": "python_backend/content.txt",
    "chars": 20,
    "preview": "文生图测试内容 - ChattyPlay"
  },
  {
    "path": "python_backend/main.py",
    "chars": 1703,
    "preview": "# -*- coding: utf-8 -*-\nimport erniebot\nfrom tools.config_tool import get_conf\nfrom tools.read_file import read\nfrom too"
  },
  {
    "path": "python_backend/requirements.txt",
    "chars": 103,
    "preview": "docx2txt==0.8\nerniebot==0.5.0\nflask == 2.3.2\ngradio==3.28.0\nsentence_transformers==2.2.2\nPillow==9.5.0\n"
  },
  {
    "path": "python_backend/tools/config_tool.py",
    "chars": 964,
    "preview": "# -*- coding: utf-8 -*-\nimport importlib\nfrom functools import lru_cache\nimport sys\nimport os\nimport socket\nfrom context"
  },
  {
    "path": "python_backend/tools/generate_video.py",
    "chars": 6044,
    "preview": "# -*- coding: utf-8 -*-\nimport cv2 as cv\nfrom PIL import Image\nimport numpy as np\nimport os\nimport math\nproject_dir = os"
  },
  {
    "path": "python_backend/tools/image_effect.py",
    "chars": 1004,
    "preview": "# -*- coding: utf-8 -*-\nfrom PIL import Image, ImageSequence, ImageEnhance\n\n\ndef add_fade_effect(image_path, duration=1)"
  },
  {
    "path": "python_backend/tools/read_file.py",
    "chars": 183,
    "preview": "# -*- coding: utf-8 -*-\ndef read(file_path):\n    \"\"\"\n        读文件\n    \"\"\"\n    f = open(file_path, \"r\", encoding='utf-8')\n"
  },
  {
    "path": "python_backend/tools/stable_api.py",
    "chars": 1944,
    "preview": "# -*- coding: utf-8 -*-\n# api文档 http://127.0.0.1:7860/docs\n# 第三方api调用库 https://github.com/mix1009/sdwebuiapi\nimport requ"
  },
  {
    "path": "src/App.tsx",
    "chars": 8089,
    "preview": "import React, { useState, useEffect } from 'react'\nimport { BrowserRouter as Router, Routes, Route, useLocation, useNavi"
  },
  {
    "path": "src/assets/css/ai.scss",
    "chars": 6375,
    "preview": ".markdown-content {\n  line-height: 1.7;\n  color: #334155;\n\n  // 标题样式\n  h1, h2, h3, h4, h5, h6 {\n    margin: 16px 0 8px;\n"
  },
  {
    "path": "src/assets/css/global.css",
    "chars": 4180,
    "preview": "* {\n  margin: 0;\n  padding: 0;\n  box-sizing: border-box;\n}\n\nbody {\n  font-family:\n    -apple-system, BlinkMacSystemFont,"
  },
  {
    "path": "src/assets/css/gold.scss",
    "chars": 14297,
    "preview": "/* 终端容器 */\n.terminal-container {\n  display: flex;\n  flex-direction: column;\n  min-height: 100vh;\n  background: linear-gr"
  },
  {
    "path": "src/assets/css/markdown.css",
    "chars": 1793,
    "preview": "#chat-markdown ol {\n  padding-left: 2em;\n}\n#chat-markdown .hljs {\n  border-radius: 5px;\n  padding: 50px 15px 15px 15px;\n"
  },
  {
    "path": "src/components/ClickEffect.tsx",
    "chars": 2993,
    "preview": "import React, { useEffect, useCallback } from 'react'\nimport styled from 'styled-components'\n\n// 社会主义核心价值观词语\nconst WORDS"
  },
  {
    "path": "src/components/Footer.tsx",
    "chars": 1131,
    "preview": "import React from 'react'\nimport { Typography } from 'antd'\nimport { useTranslation } from 'react-i18next'\nimport styled"
  },
  {
    "path": "src/components/HeartBeat.tsx",
    "chars": 7252,
    "preview": "import React, { useEffect, useRef } from 'react'\nimport * as THREE from 'three'\nimport { OBJLoader } from 'three/example"
  },
  {
    "path": "src/components/Live2DDashboard.tsx",
    "chars": 4617,
    "preview": "import React, { useEffect, useRef, useState } from 'react'\nimport styled from 'styled-components'\n\n// 全局变量\nlet isGlobalS"
  },
  {
    "path": "src/components/Navbar.tsx",
    "chars": 10935,
    "preview": "import React, { useState, useEffect } from 'react'\nimport { Menu, Button, Drawer, message, Dropdown, Avatar } from 'antd"
  },
  {
    "path": "src/components/VerifyCode.tsx",
    "chars": 2194,
    "preview": "import React, { useEffect, useRef } from 'react'\nimport styled from 'styled-components'\n\ninterface VerifyCodeProps {\n  c"
  },
  {
    "path": "src/components/VersionUpdateModal.tsx",
    "chars": 2528,
    "preview": "import React, { useEffect, useState } from 'react'\nimport { Modal, Button } from 'antd'\nimport { useTranslation } from '"
  },
  {
    "path": "src/components/goofish/GoofishLayout.tsx",
    "chars": 9116,
    "preview": "import React, { useState, useEffect } from 'react'\nimport { Layout, Menu, theme, Button, Typography, Space } from 'antd'"
  },
  {
    "path": "src/components/goofish/index.ts",
    "chars": 59,
    "preview": "export { default as GoofishLayout } from './GoofishLayout'\n"
  },
  {
    "path": "src/components/latex/FileTree.tsx",
    "chars": 19656,
    "preview": "import React, { useState, useMemo, useCallback, useEffect } from 'react'\nimport { Tree, Input, Button, Modal, message, D"
  },
  {
    "path": "src/components/latex/LatexEditor.tsx",
    "chars": 4484,
    "preview": "import React, { useEffect, useRef } from 'react'\nimport Editor, { OnMount } from '@monaco-editor/react'\nimport type { ed"
  },
  {
    "path": "src/components/latex/PdfPreview.tsx",
    "chars": 8204,
    "preview": "import React, { useState } from 'react'\nimport { Document, Page, pdfjs } from 'react-pdf'\nimport { Spin, Button, Space, "
  },
  {
    "path": "src/components/latex/tree.css",
    "chars": 623,
    "preview": "/* 覆盖 Ant Design Tree 组件的样式 */\n.ant-tree .ant-tree-node-content-wrapper,\ndiv.ant-tree .ant-tree-node-content-wrapper,\n[c"
  },
  {
    "path": "src/components/markmap/EditNodeModal.tsx",
    "chars": 905,
    "preview": "import React from 'react'\nimport { Modal, Input } from 'antd'\nimport { useTranslation } from 'react-i18next'\n\nconst { Te"
  },
  {
    "path": "src/components/markmap/EditorPanel.tsx",
    "chars": 5102,
    "preview": "import React from 'react'\nimport { Button, Input, Slider } from 'antd'\nimport { ClearOutlined } from '@ant-design/icons'"
  },
  {
    "path": "src/components/markmap/InfoModal.tsx",
    "chars": 667,
    "preview": "import React from 'react'\nimport { Modal } from 'antd'\nimport { useTranslation } from 'react-i18next'\n\ninterface InfoMod"
  },
  {
    "path": "src/components/markmap/LandscapeMode.tsx",
    "chars": 2584,
    "preview": "import React from 'react'\nimport { Button } from 'antd'\nimport { FullscreenExitOutlined } from '@ant-design/icons'\nimpor"
  },
  {
    "path": "src/components/markmap/MindmapPanel.tsx",
    "chars": 5244,
    "preview": "import React from 'react'\nimport { Button } from 'antd'\nimport {\n  FullscreenOutlined,\n  FullscreenExitOutlined,\n  FileI"
  },
  {
    "path": "src/components/markmap/MobileMenu.tsx",
    "chars": 2022,
    "preview": "import React from 'react'\nimport { Button } from 'antd'\nimport { InfoCircleOutlined, GithubOutlined } from '@ant-design/"
  },
  {
    "path": "src/components/markmap/MobileTabBar.tsx",
    "chars": 2665,
    "preview": "import React from 'react'\nimport { EditOutlined } from '@ant-design/icons'\nimport { useTranslation } from 'react-i18next"
  },
  {
    "path": "src/components/markmap/PromptModal.tsx",
    "chars": 1340,
    "preview": "import React from 'react'\nimport { Modal, Input, Button } from 'antd'\nimport { useTranslation } from 'react-i18next'\n\nco"
  },
  {
    "path": "src/components/video/VideoDownload.tsx",
    "chars": 28112,
    "preview": "import React, { useState } from 'react'\nimport { Input, Button, message, Typography, Card, Spin, Tag, Space, Divider, Pr"
  },
  {
    "path": "src/components/video/VideoParse.tsx",
    "chars": 11931,
    "preview": "import React, { useState } from 'react'\nimport { Input, Button, Select, message, Typography } from 'antd'\nimport { PlayC"
  },
  {
    "path": "src/goofish/ai-tools/chat-history.tool.ts",
    "chars": 1029,
    "preview": "/**\n * AI 聊天历史工具\n * 获取当前会话的历史消息作为上下文\n */\n\nimport { db } from '../db/connection'\n\nexport interface ChatHistoryContext {\n "
  },
  {
    "path": "src/goofish/ai-tools/index.ts",
    "chars": 417,
    "preview": "/**\n * AI 工具模块\n * 导出可供 AI 使用的工具函数和定义\n */\n\nexport {\n  queryBuyerOrders,\n  orderQueryToolDefinition,\n  type OrderQueryCont"
  },
  {
    "path": "src/goofish/ai-tools/order-query.tool.ts",
    "chars": 1965,
    "preview": "/**\n * AI 订单查询工具\n * 允许 AI 查询当前对话买家的订单信息\n */\n\nimport { db } from \"../db/connection\"\nimport { ORDER_STATUS_TEXT } from \".."
  },
  {
    "path": "src/goofish/api/index.ts",
    "chars": 218,
    "preview": "/**\n * API 模块统一导出\n */\n\nexport { startServer, setClientManager, createApp } from './server.js'\nexport { messageStore } fr"
  },
  {
    "path": "src/goofish/api/middlewares/index.ts",
    "chars": 59,
    "preview": "export { securityMiddleware } from './security.middleware'\n"
  },
  {
    "path": "src/goofish/api/middlewares/security.middleware.ts",
    "chars": 1707,
    "preview": "/**\n * 安全中间件\n * 使用 Hono 内置 CSRF 中间件限制 API 只能从同源请求\n */\n\nimport { csrf } from \"hono/csrf\";\nimport type { Context, Next } f"
  },
  {
    "path": "src/goofish/api/routes/accounts.ts",
    "chars": 5115,
    "preview": "import { Hono } from \"hono\";\n\nimport { createLogger } from \"../../core/logger\";\nimport { CookiesManager } from \"../../co"
  },
  {
    "path": "src/goofish/api/routes/auth.route.ts",
    "chars": 3050,
    "preview": "/**\n * 用户认证路由\n */\n\nimport { Hono } from 'hono'\nimport { register, login, getUserByToken } from '../../services/auth.serv"
  },
  {
    "path": "src/goofish/api/routes/autoreply.ts",
    "chars": 5524,
    "preview": "import { Hono } from \"hono\";\nimport { readFileSync } from \"fs\";\nimport { fileURLToPath } from \"url\";\nimport { dirname, j"
  },
  {
    "path": "src/goofish/api/routes/autosell.ts",
    "chars": 3485,
    "preview": "/**\n * 自动发货 API 路由\n */\n\nimport { Hono } from \"hono\";\n\nimport {\n  getAutoSellRules,\n  getAutoSellRule,\n  createAutoSellRu"
  },
  {
    "path": "src/goofish/api/routes/conversations.ts",
    "chars": 2756,
    "preview": "import { Hono } from \"hono\";\n\nimport { conversationStore } from \"../stores/conversation.store\";\nimport { fetchUserHead }"
  },
  {
    "path": "src/goofish/api/routes/dev-messages.route.ts",
    "chars": 6359,
    "preview": "import { Hono } from \"hono\";\nimport {\n  existsSync,\n  mkdirSync,\n  writeFileSync,\n  readdirSync,\n  unlinkSync,\n  statSyn"
  },
  {
    "path": "src/goofish/api/routes/goods.ts",
    "chars": 2148,
    "preview": "import { Hono } from \"hono\";\n\nimport { getAllAccounts, getAccount } from \"../../db/index\";\nimport { fetchGoodsList } fro"
  },
  {
    "path": "src/goofish/api/routes/index.ts",
    "chars": 538,
    "preview": "export { createAccountRoutes } from './accounts'\nexport { createGoodsRoutes } from './goods'\nexport { createMessageRoute"
  },
  {
    "path": "src/goofish/api/routes/logs.ts",
    "chars": 4812,
    "preview": "import { Hono } from \"hono\";\nimport fs from \"fs\";\nimport path from \"path\";\n\nconst logsDir = path.join(process.cwd(), \"lo"
  },
  {
    "path": "src/goofish/api/routes/messages.ts",
    "chars": 1217,
    "preview": "import { Hono } from \"hono\";\nimport { messageStore } from \"../stores/message.store\";\nimport type { ClientManager } from "
  },
  {
    "path": "src/goofish/api/routes/order.route.ts",
    "chars": 5394,
    "preview": "/**\n * 订单 API 路由\n */\n\nimport { Hono } from \"hono\";\n\nimport {\n  getOrderList,\n  getOrder,\n  fetchAndUpdateOrderDetail,\n} "
  },
  {
    "path": "src/goofish/api/routes/status.ts",
    "chars": 838,
    "preview": "import { Hono } from \"hono\";\nimport { messageStore } from \"../stores/message.store\";\nimport { getAllMessagesCount } from"
  },
  {
    "path": "src/goofish/api/routes/workflow.route.ts",
    "chars": 1774,
    "preview": "/**\n * 发货流程 API 路由\n */\n\nimport { Hono } from \"hono\";\n\nimport {\n  getWorkflows,\n  getWorkflowById,\n  createWorkflow,\n  up"
  },
  {
    "path": "src/goofish/api/routes/ws-push.route.ts",
    "chars": 6201,
    "preview": "/**\n * WebSocket 推送路由\n * 替代 SSE,使用 WebSocket 实现实时数据推送\n */\n\nimport { Hono } from \"hono\";\nimport type { WSContext } from \""
  },
  {
    "path": "src/goofish/api/server.ts",
    "chars": 4116,
    "preview": "import { serve } from \"@hono/node-server\";\nimport { createNodeWebSocket } from \"@hono/node-ws\";\nimport { Hono } from \"ho"
  },
  {
    "path": "src/goofish/api/stores/conversation.store.ts",
    "chars": 1232,
    "preview": "/**\n * 对话存储(兼容层)\n * 委托给 conversation.service 处理\n */\n\nimport {\n  addIncomingMessage,\n  addOutgoingMessage,\n  updateUserAv"
  },
  {
    "path": "src/goofish/api/stores/message.store.ts",
    "chars": 612,
    "preview": "/**\n * 消息存储(兼容层)\n * 委托给 message.service 处理\n */\n\nimport {\n  addMessage,\n  getRecentMessages,\n  getAllMessages,\n  getMessa"
  },
  {
    "path": "src/goofish/core/constants.ts",
    "chars": 2455,
    "preview": "// WebSocket 配置\nexport const WS_CONFIG = {\n  URL: \"wss://wss-goofish.dingtalk.com/\",\n  HEARTBEAT_INTERVAL: 15, // 心跳间隔(秒"
  },
  {
    "path": "src/goofish/core/cookies.manager.ts",
    "chars": 3135,
    "preview": "import { getAccount, updateAccountCookies } from \"../db/index\";\nimport {\n  parseCookies,\n  mergeCookies,\n  parseSetCooki"
  },
  {
    "path": "src/goofish/core/event-emitter.ts",
    "chars": 1429,
    "preview": "/**\n * 全局事件发射器\n * 用于数据变化时通知 SSE 推送\n * 使用防抖机制避免频繁触发\n */\n\nimport { EventEmitter } from \"events\";\n\nexport const appEvents ="
  },
  {
    "path": "src/goofish/core/logger.ts",
    "chars": 4025,
    "preview": "import fs from \"fs\";\nimport path from \"path\";\n\nconst logsDir = path.join(process.cwd(), \"logs\");\n\nexport type LogLevel ="
  },
  {
    "path": "src/goofish/db/account.repository.ts",
    "chars": 6059,
    "preview": "/**\n * 账号数据仓库\n */\n\nimport { db } from \"./connection\";\nimport { createLogger } from \"../core/logger\";\nimport { nowLocalSt"
  },
  {
    "path": "src/goofish/db/autoreply.repository.ts",
    "chars": 3195,
    "preview": "/**\n * 自动回复规则数据仓库\n */\n\nimport { db } from \"./connection\";\nimport type {\n  DbAutoReplyRule,\n  CreateAutoReplyRuleParams,\n"
  },
  {
    "path": "src/goofish/db/autosell.repository.ts",
    "chars": 9114,
    "preview": "/**\n * 自动发货数据仓库\n */\n\nimport { db } from \"./connection\";\nimport type {\n  DbAutoSellRule,\n  DbStockItem,\n  DbDeliveryLog,\n"
  },
  {
    "path": "src/goofish/db/connection.ts",
    "chars": 659,
    "preview": "/**\n * 数据库连接管理\n */\n\nimport Database from \"better-sqlite3\";\nimport path from \"path\";\nimport fs from \"fs\";\n\nimport { DB_CO"
  },
  {
    "path": "src/goofish/db/conversation.repository.ts",
    "chars": 4643,
    "preview": "/**\n * 对话数据仓库\n */\n\nimport { db } from \"./connection\";\nimport { emitConversationsUpdated } from \"../core/event-emitter\";\n"
  },
  {
    "path": "src/goofish/db/index.ts",
    "chars": 2118,
    "preview": "/**\n * 数据库模块统一导出\n */\n\nimport { db, closeDatabase, getDbPath } from \"./connection\";\nimport { runMigrations } from \"./migr"
  },
  {
    "path": "src/goofish/db/migrations.ts",
    "chars": 10327,
    "preview": "/**\n * 数据库迁移和表结构初始化\n */\n\nimport { db } from './connection'\nimport { createLogger } from '../core/logger'\n\nconst logger ="
  },
  {
    "path": "src/goofish/db/order.repository.ts",
    "chars": 4865,
    "preview": "/**\n * 订单数据仓库\n */\n\nimport { db } from \"./connection\";\nimport { nowLocalString } from \"../utils/date\";\nimport { emitOrder"
  },
  {
    "path": "src/goofish/db/settings.repository.ts",
    "chars": 2119,
    "preview": "/**\n * 系统设置数据仓库\n */\n\nimport { db } from \"./connection\";\n\ninterface DbSetting {\n  key: string;\n  value: string;\n  updated"
  },
  {
    "path": "src/goofish/db/user-avatar.repository.ts",
    "chars": 1364,
    "preview": "/**\n * 用户头像缓存数据仓库\n */\n\nimport { db } from \"./connection\";\nimport type { DbUserAvatar, SaveUserAvatarParams } from \"../ty"
  },
  {
    "path": "src/goofish/db/user.repository.ts",
    "chars": 3387,
    "preview": "/**\n * ChattyPlay 用户数据访问层\n */\n\nimport { db } from './connection'\nimport { createLogger } from '../core/logger'\n\nconst lo"
  },
  {
    "path": "src/goofish/db/workflow.repository.ts",
    "chars": 7708,
    "preview": "/**\n * 发货流程数据仓库\n */\n\nimport { db } from \"./connection\";\nimport type {\n  Workflow,\n  WorkflowDefinition,\n  WorkflowExecut"
  },
  {
    "path": "src/goofish/index.ts",
    "chars": 2582,
    "preview": "import {\n  createLogger,\n  cleanOldLogs,\n  setLogLevel,\n  LogLevel,\n} from \"./core/logger\";\nimport { ClientManager } fro"
  },
  {
    "path": "src/goofish/services/ai.service.ts",
    "chars": 6479,
    "preview": "/**\n * AI 回复服务\n */\n\nimport OpenAI from \"openai\";\n\nimport { createLogger } from \"../core/logger\";\nimport { getAISettings "
  },
  {
    "path": "src/goofish/services/auth.service.ts",
    "chars": 4048,
    "preview": "/**\n * 用户认证服务\n */\n\nimport { userRepository, ChattyplayUser, UserWithoutPassword } from '../db/user.repository'\nimport { "
  },
  {
    "path": "src/goofish/services/autoreply.service.ts",
    "chars": 4032,
    "preview": "/**\n * 自动回复服务\n */\n\nimport { createLogger } from \"../core/logger\";\nimport { getEnabledAutoReplyRules } from \"../db/index\""
  },
  {
    "path": "src/goofish/services/autosell.service.ts",
    "chars": 4133,
    "preview": "/**\n * 自动发货服务\n */\n\nimport { createLogger } from \"../core/logger\";\nimport {\n  getEnabledAutoSellRules,\n  getStockStats,\n "
  },
  {
    "path": "src/goofish/services/conversation.service.ts",
    "chars": 3951,
    "preview": "/**\n * 对话服务\n * 处理对话相关的业务逻辑\n */\n\nimport {\n  getConversations,\n  getConversation,\n  upsertConversation,\n  updateConversati"
  },
  {
    "path": "src/goofish/services/goods.service.ts",
    "chars": 3411,
    "preview": "/**\n * 商品服务\n */\n\nimport { API_ENDPOINTS, WS_CONFIG } from \"../core/constants\";\nimport { CookiesManager } from \"../core/c"
  },
  {
    "path": "src/goofish/services/index.ts",
    "chars": 1004,
    "preview": "/**\n * 服务层统一导出\n */\n\n// 自动回复服务\nexport { checkAutoReply, getMatchTypeName } from \"./autoreply.service\";\n\n// 对话服务\nexport {\n"
  },
  {
    "path": "src/goofish/services/message.service.ts",
    "chars": 814,
    "preview": "/**\n * 消息服务\n * 处理消息存储和查询\n */\n\nimport type { StoredMessage, ChatMessage } from \"../types/index\";\n\nconst MAX_MESSAGES = 10"
  },
  {
    "path": "src/goofish/services/order.service.ts",
    "chars": 5229,
    "preview": "/**\n * 订单服务\n * 简化版:订单ID唯一,通过API获取订单详情\n */\n\nimport { createLogger } from \"../core/logger\";\nimport {\n  getOrders,\n  getOrd"
  },
  {
    "path": "src/goofish/services/user.service.ts",
    "chars": 7934,
    "preview": "/**\n * 用户服务\n */\n\nimport { API_ENDPOINTS, WS_CONFIG } from \"../core/constants\";\nimport { CookiesManager } from \"../core/c"
  },
  {
    "path": "src/goofish/services/workflow.service.ts",
    "chars": 12362,
    "preview": "/**\n * 流程执行引擎\n * 负责执行发货流程,支持自动回复节点等待用户确认\n */\n\nimport { createLogger } from \"../core/logger\";\nimport {\n  getWorkflowById,"
  },
  {
    "path": "src/goofish/types/account.types.ts",
    "chars": 972,
    "preview": "/**\n * 账号相关类型定义\n */\n\nimport type { Timestamped } from \"./common.types\";\n\n// 账号信息\nexport interface Account extends Timest"
  },
  {
    "path": "src/goofish/types/autoreply.types.ts",
    "chars": 1324,
    "preview": "/**\n * 自动回复相关类型定义\n */\n\n// 匹配类型\nexport type MatchType = \"exact\" | \"contains\" | \"regex\" | \"ai\";\n\n// 自动回复规则\nexport interfac"
  },
  {
    "path": "src/goofish/types/autosell.types.ts",
    "chars": 2631,
    "preview": "/**\n * 自动发货相关类型定义\n */\n\n// 发货类型\nexport type DeliveryType = \"fixed\" | \"stock\" | \"api\";\n\n// 触发时机\nexport type TriggerOn = \"p"
  },
  {
    "path": "src/goofish/types/common.types.ts",
    "chars": 433,
    "preview": "/**\n * 通用类型定义\n */\n\n// 分页参数\nexport interface PaginationParams {\n  limit: number;\n  offset: number;\n}\n\n// 分页结果\nexport inte"
  },
  {
    "path": "src/goofish/types/conversation.types.ts",
    "chars": 1340,
    "preview": "/**\n * 对话相关类型定义\n */\n\nimport type { ConversationMessage, MessageDirection } from \"./message.types\";\n\n// 对话信息\nexport inter"
  },
  {
    "path": "src/goofish/types/goods.types.ts",
    "chars": 447,
    "preview": "/**\n * 商品相关类型定义\n */\n\n// 商品信息\nexport interface GoodsItem {\n  id: string;\n  title: string;\n  price: string;\n  picUrl: stri"
  },
  {
    "path": "src/goofish/types/index.ts",
    "chars": 416,
    "preview": "// 通用类型\nexport * from './common.types'\n\n// 账号类型\nexport * from './account.types'\n\n// 消息类型\nexport * from './message.types'"
  },
  {
    "path": "src/goofish/types/message.types.ts",
    "chars": 747,
    "preview": "/**\n * 消息相关类型定义\n */\n\n// 聊天消息\nexport interface ChatMessage {\n  senderId: string;\n  senderName: string;\n  msgTime: string;"
  },
  {
    "path": "src/goofish/types/order.types.ts",
    "chars": 1769,
    "preview": "/**\n * 订单相关类型定义\n */\n\n// 订单状态枚举\nexport enum OrderStatus {\n  PENDING_PAYMENT = 1, // 待付款\n  PENDING_SHIPMENT = 2, // 待发货\n  "
  },
  {
    "path": "src/goofish/types/user.types.ts",
    "chars": 556,
    "preview": "/**\n * 用户相关类型定义\n */\n\n// 用户头像信息\nexport interface UserHeadInfo {\n  userId: string;\n  displayName: string;\n  avatar: string"
  },
  {
    "path": "src/goofish/types/workflow.types.ts",
    "chars": 2809,
    "preview": "/**\n * 发货流程相关类型定义\n */\n\n// 流程节点类型\nexport type WorkflowNodeType =\n  | \"trigger\" // 触发节点(开始)\n  | \"autoreply\" // 自动回复节点(等待用户"
  },
  {
    "path": "src/goofish/utils/cookies.ts",
    "chars": 1593,
    "preview": "// 解析 cookies 字符串为对象\nexport function parseCookies(cookiesStr: string): Record<string, string> {\n  if (!cookiesStr) retur"
  },
  {
    "path": "src/goofish/utils/crypto.ts",
    "chars": 1287,
    "preview": "import crypto from \"crypto\";\n\nexport function generateMid(): string {\n  const randomPart = Math.floor(Math.random() * 10"
  },
  {
    "path": "src/goofish/utils/date.ts",
    "chars": 526,
    "preview": "/**\n * 日期时间工具函数\n */\n\n// 获取当前本地时间字符串(格式:YYYY-MM-DD HH:mm:ss)\nexport function nowLocalString(): string {\n  const now = new"
  },
  {
    "path": "src/goofish/utils/index.ts",
    "chars": 249,
    "preview": "/**\n * 工具函数统一导出\n */\n\nexport { generateMid, generateUuid, generateDeviceId, generateSign } from './crypto'\nexport { parse"
  },
  {
    "path": "src/goofish/utils/jwt.util.ts",
    "chars": 2174,
    "preview": "/**\n * JWT 工具\n * 简单的 token 生成和验证\n */\n\nimport CryptoJS from 'crypto-js'\n\nconst JWT_SECRET = 'chattyplay-jwt-secret-2024'\n"
  },
  {
    "path": "src/goofish/utils/msgpack.ts",
    "chars": 3124,
    "preview": "// MessagePack 解码器\nclass MessagePackDecoder {\n  private data: Buffer;\n  private pos = 0;\n\n  constructor(data: Buffer) {\n"
  },
  {
    "path": "src/goofish/utils/password.util.ts",
    "chars": 558,
    "preview": "/**\n * 密码加密工具\n * 使用 crypto-js 进行密码哈希\n */\n\nimport CryptoJS from 'crypto-js'\n\nconst SECRET_KEY = 'chattyplay-secret-key-20"
  },
  {
    "path": "src/goofish/websocket/client.manager.ts",
    "chars": 3087,
    "preview": "import { GoofishClient } from \"./client\";\nimport { createLogger } from \"../core/logger\";\nimport { getEnabledAccounts, up"
  },
  {
    "path": "src/goofish/websocket/client.ts",
    "chars": 15124,
    "preview": "import WebSocket from \"ws\";\nimport { WS_CONFIG, WS_HEADERS, API_ENDPOINTS } from \"../core/constants\";\nimport { createLog"
  },
  {
    "path": "src/goofish/websocket/index.ts",
    "chars": 373,
    "preview": "/**\n * WebSocket 模块统一导出\n */\n\nexport { GoofishClient } from './client'\nexport { ClientManager } from './client.manager'\ne"
  },
  {
    "path": "src/goofish/websocket/message.parser.ts",
    "chars": 7356,
    "preview": "import { decryptMessagePack } from \"../utils/msgpack\";\nimport { createLogger } from \"../core/logger\";\nimport type { Chat"
  },
  {
    "path": "src/goofish/websocket/message.receiver.ts",
    "chars": 4800,
    "preview": "import { createLogger } from \"../core/logger\";\nimport {\n  decryptSyncData,\n  extractChatMessage,\n  isOrderStatusMessage,"
  },
  {
    "path": "src/goofish/websocket/message.sender.ts",
    "chars": 1750,
    "preview": "import WebSocket from \"ws\";\nimport { createLogger } from \"../core/logger\";\nimport { generateMid, generateUuid } from \".."
  },
  {
    "path": "src/goofish/websocket/token.ts",
    "chars": 6931,
    "preview": "import { WS_CONFIG, API_ENDPOINTS, PASSPORT_CONFIG } from \"../core/constants\";\nimport { CookiesManager } from \"../core/c"
  },
  {
    "path": "src/hooks/goofish/index.ts",
    "chars": 53,
    "preview": "export { useGoofishWebSocket } from './useWebSocket'\n"
  },
  {
    "path": "src/hooks/goofish/useWebSocket.ts",
    "chars": 2392,
    "preview": "import { useEffect, useState, useRef } from 'react'\nimport { statusApi } from '@/services/goofish'\n\ninterface UseWebSock"
  },
  {
    "path": "src/hooks/useChangeLanguage.ts",
    "chars": 373,
    "preview": "import { useCallback } from 'react'\nimport { useTranslation } from 'react-i18next'\n\nexport const useChangeLanguage = () "
  },
  {
    "path": "src/i18n/config.ts",
    "chars": 1338,
    "preview": "import i18n from 'i18next'\nimport { initReactI18next } from 'react-i18next'\nimport LanguageDetector from 'i18next-browse"
  },
  {
    "path": "src/i18n/locales/en/about.ts",
    "chars": 1996,
    "preview": "export default {\n  title: 'About Me (Looking for referrals~)',\n  role: 'A full-stack technology enthusiast',\n  name: '0x"
  },
  {
    "path": "src/i18n/locales/en/aimarkmap.ts",
    "chars": 10161,
    "preview": "const aimarkmap = {\n  promptSettingsBtn: '📝 Prompt Settings',\n  apiSettingsBtn: '⚙️ API Settings',\n  modelLabel: 'Model:"
  },
  {
    "path": "src/i18n/locales/en/cartoon.ts",
    "chars": 808,
    "preview": "export default {\n  title: 'Cartoon',\n  subTitle: 'Mercury Cartoon',\n  description: 'Discover more exciting cartoon works"
  },
  {
    "path": "src/i18n/locales/en/cartoonChapter.ts",
    "chars": 332,
    "preview": "export default {\n  backToDetail: 'Back to Detail',\n  noContent: 'No chapter content found',\n  fetchChapterFailed: 'Faile"
  },
  {
    "path": "src/i18n/locales/en/cartoonDetail.ts",
    "chars": 311,
    "preview": "export default {\n  backToList: 'Back to List',\n  notFound: 'No cartoon information found',\n  fetchDetailFailed: 'Failed "
  },
  {
    "path": "src/i18n/locales/en/common.ts",
    "chars": 742,
    "preview": "export default {\n  chinese: '中文',\n  english: 'English',\n  logoutSuccess: 'Logged out successfully',\n  loading: 'Loading."
  },
  {
    "path": "src/i18n/locales/en/gold.ts",
    "chars": 1275,
    "preview": "export default {\n  loading: 'Fetching real-time data...',\n  domesticGold: 'Domestic Gold',\n  internationalGold: 'Interna"
  },
  {
    "path": "src/i18n/locales/en/goofish.ts",
    "chars": 323,
    "preview": "const goofish = {\n  data: 'Goofish Data',\n  accounts: 'Accounts',\n  conversations: 'Conversations',\n  orders: 'Orders',\n"
  },
  {
    "path": "src/i18n/locales/en/gpt.ts",
    "chars": 1853,
    "preview": "export default {\n  title: 'ChatGPT',\n  welcomeMessage: 'Hello! I am ChattyPlay AI assistant. I can help you with any que"
  },
  {
    "path": "src/i18n/locales/en/help.ts",
    "chars": 2441,
    "preview": "export default {\n  title: 'Help',\n  description: 'Detailed tutorials and documentation to help you get started',\n  comin"
  },
  {
    "path": "src/i18n/locales/en/home.ts",
    "chars": 2522,
    "preview": "export default {\n  welcome: 'Welcome to ChattyPlay!',\n  description: 'A platform integrating multiple intelligent tools,"
  },
  {
    "path": "src/i18n/locales/en/latex.ts",
    "chars": 574,
    "preview": "const latex = {\n  title: 'LaTeX Editor',\n  compile: 'Compile',\n  compiling: 'Compiling...',\n  compileSuccess: 'Compile s"
  },
  {
    "path": "src/i18n/locales/en/login.ts",
    "chars": 1743,
    "preview": "export default {\n  account: 'Account',\n  accountPlaceholder: 'Enter your ChattyPlay account',\n  accountRequired: 'Please"
  },
  {
    "path": "src/i18n/locales/en/music.ts",
    "chars": 593,
    "preview": "export default {\n  title: 'Music Parse',\n  subTitle: 'Music',\n  description: 'Enter song name to play online, supports m"
  },
  {
    "path": "src/i18n/locales/en/nav.ts",
    "chars": 428,
    "preview": "const nav = {\n  home: 'Home',\n  chatgpt: 'ChatGPT',\n  video: 'Video',\n  music: 'Music',\n  cartoon: 'Cartoon',\n  paper: '"
  },
  {
    "path": "src/i18n/locales/en/notFound.ts",
    "chars": 126,
    "preview": "export default {\n  title: '404',\n  description: 'Sorry, the page you visited does not exist',\n  backHomeBtn: 'Back to Ho"
  },
  {
    "path": "src/i18n/locales/en/paper.ts",
    "chars": 1271,
    "preview": "export default {\n  title: 'AI Papers',\n  content: 'Explore the latest AI research papers, models, datasets, and applicat"
  },
  {
    "path": "src/i18n/locales/en/textToPhoto.ts",
    "chars": 1737,
    "preview": "export default {\n  title: 'Text to Photo',\n  description: 'Use Stable Diffusion API to generate beautiful images from te"
  },
  {
    "path": "src/i18n/locales/en/trans.ts",
    "chars": 1005,
    "preview": "export default {\n  title: 'Online Translation',\n  description: 'Support multiple language translation, fast and accurate"
  },
  {
    "path": "src/i18n/locales/en/versionUpdate.ts",
    "chars": 335,
    "preview": "export default {\n  title: 'New Version Available',\n  message: 'A new version has been detected!',\n  currentVersion: 'Cur"
  },
  {
    "path": "src/i18n/locales/en/video.ts",
    "chars": 1693,
    "preview": "export default {\n  tabParse: 'Video Parse',\n  tabDownload: 'Video Download',\n  title: 'Video Parse',\n  description: 'Ent"
  },
  {
    "path": "src/i18n/locales/en.ts",
    "chars": 1030,
    "preview": "import nav from './en/nav'\nimport home from './en/home'\nimport login from './en/login'\nimport video from './en/video'\nim"
  },
  {
    "path": "src/i18n/locales/zh/about.ts",
    "chars": 958,
    "preview": "const about = {\n  title: '关于我(求内推~)',\n  role: '一名全栈技术的爱好者',\n  name: '不见水星记(研一',\n  description: '宇宙终极无敌超级爆炸究极飞天帅比暴龙战士',\n "
  },
  {
    "path": "src/i18n/locales/zh/aimarkmap.ts",
    "chars": 6067,
    "preview": "const aimarkmap = {\n  promptSettingsBtn: '📝 Prompt设置',\n  apiSettingsBtn: '⚙️ API设置',\n  modelLabel: '模型:',\n  modelLabelMo"
  }
]

// ... and 85 more files (download for full content)

About this extraction

This page contains the full source code of the P1kaj1uu/VIP-Video-Parsing GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 285 files (2.4 MB), approximately 643.9k tokens, and a symbol index with 919 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!