From 6cb198203d3d01acb9d92da363ffbcf500462547 Mon Sep 17 00:00:00 2001 From: ZhangAo Date: Wed, 10 Jun 2026 17:31:34 +0800 Subject: [PATCH] 20260610173132 --- code/ai/PP-StructureV3.yaml | 235 ++++ code/ai/ai-springboot/.gitattributes | 2 + code/ai/ai-springboot/.gitignore | 33 + .../.mvn/wrapper/maven-wrapper.properties | 3 + code/ai/ai-springboot/mvnw | 295 ++++ code/ai/ai-springboot/mvnw.cmd | 189 +++ code/ai/ai-springboot/pom.xml | 129 ++ .../cn/g3soft/AiSpringbootApplication.java | 13 + .../g3soft/config/FileStorageProperties.java | 82 ++ .../java/cn/g3soft/config/JacksonConfig.java | 14 + .../g3soft/config/PpStructureProperties.java | 81 ++ .../java/cn/g3soft/config/WebMvcConfig.java | 39 + .../cn/g3soft/file/FileStorageService.java | 69 + .../cn/g3soft/file/FileUploadController.java | 23 + .../cn/g3soft/file/FileUploadResponse.java | 10 + .../recognition/RecognitionController.java | 22 + .../recognition/RecognitionRequest.java | 7 + .../recognition/RecognitionResponse.java | 12 + .../recognition/RecognitionService.java | 146 ++ .../src/main/resources/application.yml | 20 + .../ai/AiSpringbootApplicationTests.java | 13 + code/ai/ai-vue/.env.development | 1 + code/ai/ai-vue/.gitignore | 24 + code/ai/ai-vue/.oxfmtrc.json | 4 + code/ai/ai-vue/.vscode/extensions.json | 3 + code/ai/ai-vue/README.md | 5 + code/ai/ai-vue/index.html | 13 + code/ai/ai-vue/package.json | 27 + code/ai/ai-vue/pnpm-lock.yaml | 1205 +++++++++++++++++ code/ai/ai-vue/public/favicon.svg | 1 + code/ai/ai-vue/public/icons.svg | 24 + code/ai/ai-vue/src/App.vue | 425 ++++++ code/ai/ai-vue/src/api/file.ts | 62 + code/ai/ai-vue/src/assets/hero.png | Bin 0 -> 13057 bytes code/ai/ai-vue/src/assets/vite.svg | 1 + code/ai/ai-vue/src/assets/vue.svg | 1 + code/ai/ai-vue/src/components/HelloWorld.vue | 93 ++ code/ai/ai-vue/src/main.ts | 6 + code/ai/ai-vue/src/style.css | 27 + code/ai/ai-vue/tsconfig.app.json | 14 + code/ai/ai-vue/tsconfig.json | 4 + code/ai/ai-vue/tsconfig.node.json | 24 + code/ai/ai-vue/vite.config.ts | 24 + .../9a6506af761a427490b5392dc18c8cc5.jpg | Bin 0 -> 38809 bytes .../ba1e6df6b8e04f0baaad991b33602b1b.pdf | Bin 0 -> 37544 bytes code/docusaurus/docs/resources.mdx | 510 +------ code/docusaurus/docusaurus.config.ts | 5 - .../src/components/Link/LinkCard.tsx | 29 +- .../src/components/Link/LinkGrid.tsx | 3 +- .../components/Link/SearchableLinkGrid.tsx | 6 - .../src/components/Link/styles.module.css | 281 ++-- code/docusaurus/src/css/custom.css | 201 +-- code/docusaurus/src/data/resources.ts | 584 ++++++++ code/docusaurus/src/pages/index.module.css | 311 +---- code/docusaurus/src/pages/index.tsx | 87 +- 新建文本文档.txt | 33 + 56 files changed, 4265 insertions(+), 1210 deletions(-) create mode 100644 code/ai/PP-StructureV3.yaml create mode 100644 code/ai/ai-springboot/.gitattributes create mode 100644 code/ai/ai-springboot/.gitignore create mode 100644 code/ai/ai-springboot/.mvn/wrapper/maven-wrapper.properties create mode 100644 code/ai/ai-springboot/mvnw create mode 100644 code/ai/ai-springboot/mvnw.cmd create mode 100644 code/ai/ai-springboot/pom.xml create mode 100644 code/ai/ai-springboot/src/main/java/cn/g3soft/AiSpringbootApplication.java create mode 100644 code/ai/ai-springboot/src/main/java/cn/g3soft/config/FileStorageProperties.java create mode 100644 code/ai/ai-springboot/src/main/java/cn/g3soft/config/JacksonConfig.java create mode 100644 code/ai/ai-springboot/src/main/java/cn/g3soft/config/PpStructureProperties.java create mode 100644 code/ai/ai-springboot/src/main/java/cn/g3soft/config/WebMvcConfig.java create mode 100644 code/ai/ai-springboot/src/main/java/cn/g3soft/file/FileStorageService.java create mode 100644 code/ai/ai-springboot/src/main/java/cn/g3soft/file/FileUploadController.java create mode 100644 code/ai/ai-springboot/src/main/java/cn/g3soft/file/FileUploadResponse.java create mode 100644 code/ai/ai-springboot/src/main/java/cn/g3soft/recognition/RecognitionController.java create mode 100644 code/ai/ai-springboot/src/main/java/cn/g3soft/recognition/RecognitionRequest.java create mode 100644 code/ai/ai-springboot/src/main/java/cn/g3soft/recognition/RecognitionResponse.java create mode 100644 code/ai/ai-springboot/src/main/java/cn/g3soft/recognition/RecognitionService.java create mode 100644 code/ai/ai-springboot/src/main/resources/application.yml create mode 100644 code/ai/ai-springboot/src/test/java/cn/g3soft/ai/AiSpringbootApplicationTests.java create mode 100644 code/ai/ai-vue/.env.development create mode 100644 code/ai/ai-vue/.gitignore create mode 100644 code/ai/ai-vue/.oxfmtrc.json create mode 100644 code/ai/ai-vue/.vscode/extensions.json create mode 100644 code/ai/ai-vue/README.md create mode 100644 code/ai/ai-vue/index.html create mode 100644 code/ai/ai-vue/package.json create mode 100644 code/ai/ai-vue/pnpm-lock.yaml create mode 100644 code/ai/ai-vue/public/favicon.svg create mode 100644 code/ai/ai-vue/public/icons.svg create mode 100644 code/ai/ai-vue/src/App.vue create mode 100644 code/ai/ai-vue/src/api/file.ts create mode 100644 code/ai/ai-vue/src/assets/hero.png create mode 100644 code/ai/ai-vue/src/assets/vite.svg create mode 100644 code/ai/ai-vue/src/assets/vue.svg create mode 100644 code/ai/ai-vue/src/components/HelloWorld.vue create mode 100644 code/ai/ai-vue/src/main.ts create mode 100644 code/ai/ai-vue/src/style.css create mode 100644 code/ai/ai-vue/tsconfig.app.json create mode 100644 code/ai/ai-vue/tsconfig.json create mode 100644 code/ai/ai-vue/tsconfig.node.json create mode 100644 code/ai/ai-vue/vite.config.ts create mode 100644 code/ai/uploads/9a6506af761a427490b5392dc18c8cc5.jpg create mode 100644 code/ai/uploads/ba1e6df6b8e04f0baaad991b33602b1b.pdf create mode 100644 code/docusaurus/src/data/resources.ts create mode 100644 新建文本文档.txt diff --git a/code/ai/PP-StructureV3.yaml b/code/ai/PP-StructureV3.yaml new file mode 100644 index 00000000..62278e98 --- /dev/null +++ b/code/ai/PP-StructureV3.yaml @@ -0,0 +1,235 @@ + +pipeline_name: PP-StructureV3 + +batch_size: 8 + +use_doc_preprocessor: False +use_seal_recognition: False +use_table_recognition: True +use_formula_recognition: True +use_chart_recognition: False +use_region_detection: True +format_block_content: False +markdown_ignore_labels: + - number + - footnote + - header + - header_image + - footer + - footer_image + - aside_text + +SubModules: + LayoutDetection: + module_name: layout_detection + model_name: PP-DocLayout_plus-L + model_dir: null + batch_size: 8 + threshold: + 0: 0.3 # paragraph_title + 1: 0.5 # image + 2: 0.4 # text + 3: 0.5 # number + 4: 0.5 # abstract + 5: 0.5 # content + 6: 0.5 # figure_table_chart_title + 7: 0.3 # formula + 8: 0.5 # table + 9: 0.5 # reference + 10: 0.5 # doc_title + 11: 0.5 # footnote + 12: 0.5 # header + 13: 0.5 # algorithm + 14: 0.5 # footer + 15: 0.45 # seal + 16: 0.5 # chart + 17: 0.5 # formula_number + 18: 0.5 # aside_text + 19: 0.5 # reference_content + layout_nms: True + layout_unclip_ratio: [1.0, 1.0] + layout_merge_bboxes_mode: + 0: "large" # paragraph_title + 1: "large" # image + 2: "union" # text + 3: "union" # number + 4: "union" # abstract + 5: "union" # content + 6: "union" # figure_table_chart_title + 7: "large" # formula + 8: "union" # table + 9: "union" # reference + 10: "union" # doc_title + 11: "union" # footnote + 12: "union" # header + 13: "union" # algorithm + 14: "union" # footer + 15: "union" # seal + 16: "large" # chart + 17: "union" # formula_number + 18: "union" # aside_text + 19: "union" # reference_content + ChartRecognition: + module_name: chart_recognition + model_name: PP-Chart2Table + model_dir: null + batch_size: 1 + RegionDetection: + module_name: layout_detection + model_name: PP-DocBlockLayout + model_dir: null + layout_nms: True + layout_merge_bboxes_mode: "small" + +SubPipelines: + DocPreprocessor: + pipeline_name: doc_preprocessor + batch_size: 8 + use_doc_orientation_classify: True + use_doc_unwarping: True + SubModules: + DocOrientationClassify: + module_name: doc_text_orientation + model_name: PP-LCNet_x1_0_doc_ori + model_dir: null + batch_size: 8 + DocUnwarping: + module_name: image_unwarping + model_name: UVDoc + model_dir: null + + GeneralOCR: + pipeline_name: OCR + batch_size: 8 + text_type: general + use_doc_preprocessor: False + use_textline_orientation: True + SubModules: + TextDetection: + module_name: text_detection + model_name: PP-OCRv5_server_det + model_dir: null + limit_side_len: 736 + limit_type: min + max_side_limit: 4000 + thresh: 0.3 + box_thresh: 0.6 + unclip_ratio: 1.5 + TextLineOrientation: + module_name: textline_orientation + model_name: PP-LCNet_x1_0_textline_ori + model_dir: null + batch_size: 8 + TextRecognition: + module_name: text_recognition + model_name: PP-OCRv5_server_rec + model_dir: null + batch_size: 8 + score_thresh: 0.0 + + + TableRecognition: + pipeline_name: table_recognition_v2 + use_layout_detection: False + use_doc_preprocessor: False + use_ocr_model: False + SubModules: + TableClassification: + module_name: table_classification + model_name: PP-LCNet_x1_0_table_cls + model_dir: null + + WiredTableStructureRecognition: + module_name: table_structure_recognition + model_name: SLANeXt_wired + model_dir: null + + WirelessTableStructureRecognition: + module_name: table_structure_recognition + model_name: SLANet_plus + model_dir: null + + WiredTableCellsDetection: + module_name: table_cells_detection + model_name: RT-DETR-L_wired_table_cell_det + model_dir: null + + WirelessTableCellsDetection: + module_name: table_cells_detection + model_name: RT-DETR-L_wireless_table_cell_det + model_dir: null + + TableOrientationClassify: + module_name: doc_text_orientation + model_name: PP-LCNet_x1_0_doc_ori + model_dir: null + SubPipelines: + GeneralOCR: + pipeline_name: OCR + text_type: general + use_doc_preprocessor: False + use_textline_orientation: True + SubModules: + TextDetection: + module_name: text_detection + model_name: PP-OCRv5_server_det + model_dir: null + limit_side_len: 736 + limit_type: min + max_side_limit: 4000 + thresh: 0.3 + box_thresh: 0.4 + unclip_ratio: 1.5 + TextLineOrientation: + module_name: textline_orientation + model_name: PP-LCNet_x1_0_textline_ori + model_dir: null + batch_size: 8 + TextRecognition: + module_name: text_recognition + model_name: PP-OCRv5_server_rec + model_dir: null + batch_size: 8 + score_thresh: 0.0 + + SealRecognition: + pipeline_name: seal_recognition + batch_size: 8 + use_layout_detection: False + use_doc_preprocessor: False + SubPipelines: + SealOCR: + pipeline_name: OCR + batch_size: 8 + text_type: seal + use_doc_preprocessor: False + use_textline_orientation: False + SubModules: + TextDetection: + module_name: seal_text_detection + model_name: PP-OCRv4_server_seal_det + model_dir: null + limit_side_len: 736 + limit_type: min + max_side_limit: 4000 + thresh: 0.2 + box_thresh: 0.6 + unclip_ratio: 0.5 + TextRecognition: + module_name: text_recognition + model_name: PP-OCRv5_server_rec + model_dir: null + batch_size: 8 + score_thresh: 0 + + FormulaRecognition: + pipeline_name: formula_recognition + batch_size: 8 + use_layout_detection: False + use_doc_preprocessor: False + SubModules: + FormulaRecognition: + module_name: formula_recognition + model_name: PP-FormulaNet_plus-L + model_dir: null + batch_size: 8 diff --git a/code/ai/ai-springboot/.gitattributes b/code/ai/ai-springboot/.gitattributes new file mode 100644 index 00000000..3b41682a --- /dev/null +++ b/code/ai/ai-springboot/.gitattributes @@ -0,0 +1,2 @@ +/mvnw text eol=lf +*.cmd text eol=crlf diff --git a/code/ai/ai-springboot/.gitignore b/code/ai/ai-springboot/.gitignore new file mode 100644 index 00000000..667aaef0 --- /dev/null +++ b/code/ai/ai-springboot/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/code/ai/ai-springboot/.mvn/wrapper/maven-wrapper.properties b/code/ai/ai-springboot/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..216df058 --- /dev/null +++ b/code/ai/ai-springboot/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,3 @@ +wrapperVersion=3.3.4 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.16/apache-maven-3.9.16-bin.zip diff --git a/code/ai/ai-springboot/mvnw b/code/ai/ai-springboot/mvnw new file mode 100644 index 00000000..bd8896bf --- /dev/null +++ b/code/ai/ai-springboot/mvnw @@ -0,0 +1,295 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.4 +# +# Optional ENV vars +# ----------------- +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output +# ---------------------------------------------------------------------------- + +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x + +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac + +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + if [ -n "${JAVA_HOME-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi + fi + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi + fi +} + +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" + done + printf %x\\n $h +} + +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } + +die() { + printf %s\\n "$1" >&2 + exit 1 +} + +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + +scriptDir="$(dirname "$0")" +scriptName="$(basename "$0")" + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} + +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" +fi + +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" +fi + +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" +fi + +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi + +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +actualDistributionDir="" + +# First try the expected directory name (for regular distributions) +if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then + if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then + actualDistributionDir="$distributionUrlNameMain" + fi +fi + +# If not found, search for any directory with the Maven executable (for snapshots) +if [ -z "$actualDistributionDir" ]; then + # enable globbing to iterate over items + set +f + for dir in "$TMP_DOWNLOAD_DIR"/*; do + if [ -d "$dir" ]; then + if [ -f "$dir/bin/$MVN_CMD" ]; then + actualDistributionDir="$(basename "$dir")" + break + fi + fi + done + set -f +fi + +if [ -z "$actualDistributionDir" ]; then + verbose "Contents of $TMP_DOWNLOAD_DIR:" + verbose "$(ls -la "$TMP_DOWNLOAD_DIR")" + die "Could not find Maven distribution directory in extracted archive" +fi + +verbose "Found extracted Maven distribution directory: $actualDistributionDir" +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/code/ai/ai-springboot/mvnw.cmd b/code/ai/ai-springboot/mvnw.cmd new file mode 100644 index 00000000..92450f93 --- /dev/null +++ b/code/ai/ai-springboot/mvnw.cmd @@ -0,0 +1,189 @@ +<# : batch portion +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.3.4 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' + +$MAVEN_M2_PATH = "$HOME/.m2" +if ($env:MAVEN_USER_HOME) { + $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME" +} + +if (-not (Test-Path -Path $MAVEN_M2_PATH)) { + New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null +} + +$MAVEN_WRAPPER_DISTS = $null +if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) { + $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists" +} else { + $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists" +} + +$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain" +$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +$actualDistributionDir = "" + +# First try the expected directory name (for regular distributions) +$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain" +$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD" +if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) { + $actualDistributionDir = $distributionUrlNameMain +} + +# If not found, search for any directory with the Maven executable (for snapshots) +if (!$actualDistributionDir) { + Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object { + $testPath = Join-Path $_.FullName "bin/$MVN_CMD" + if (Test-Path -Path $testPath -PathType Leaf) { + $actualDistributionDir = $_.Name + } + } +} + +if (!$actualDistributionDir) { + Write-Error "Could not find Maven distribution directory in extracted archive" +} + +Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir" +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/code/ai/ai-springboot/pom.xml b/code/ai/ai-springboot/pom.xml new file mode 100644 index 00000000..2f3c793f --- /dev/null +++ b/code/ai/ai-springboot/pom.xml @@ -0,0 +1,129 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 4.0.6 + + + cn.g3soft + ai-springboot + 0.0.1-SNAPSHOT + ai-springboot + ai-springboot + + + + + + + + + + + + + + + 21 + 2.0.0-RC1 + + + + org.springframework.boot + spring-boot-starter-webmvc + + + org.springframework.ai + spring-ai-starter-model-ollama + + + org.springframework.ai + spring-ai-starter-model-openai + + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-webmvc-test + test + + + + + + org.springframework.ai + spring-ai-bom + ${spring-ai.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + default-compile + compile + + compile + + + + + org.projectlombok + lombok + + + + + + default-testCompile + test-compile + + testCompile + + + + + org.projectlombok + lombok + + + + + + + + + + diff --git a/code/ai/ai-springboot/src/main/java/cn/g3soft/AiSpringbootApplication.java b/code/ai/ai-springboot/src/main/java/cn/g3soft/AiSpringbootApplication.java new file mode 100644 index 00000000..d50191b3 --- /dev/null +++ b/code/ai/ai-springboot/src/main/java/cn/g3soft/AiSpringbootApplication.java @@ -0,0 +1,13 @@ +package cn.g3soft; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class AiSpringbootApplication { + + public static void main(String[] args) { + SpringApplication.run(AiSpringbootApplication.class, args); + } + +} diff --git a/code/ai/ai-springboot/src/main/java/cn/g3soft/config/FileStorageProperties.java b/code/ai/ai-springboot/src/main/java/cn/g3soft/config/FileStorageProperties.java new file mode 100644 index 00000000..e8b9d366 --- /dev/null +++ b/code/ai/ai-springboot/src/main/java/cn/g3soft/config/FileStorageProperties.java @@ -0,0 +1,82 @@ +package cn.g3soft.config; + +import java.nio.file.Path; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +@Component +@ConfigurationProperties(prefix = "app.file") +public class FileStorageProperties { + + private String uploadDir = "uploads"; + + private String publicBaseUrl = ""; + + private String accessUrlPrefix = "/uploads"; + + public String getUploadDir() { + return uploadDir; + } + + public void setUploadDir(String uploadDir) { + this.uploadDir = uploadDir; + } + + public String getAccessUrlPrefix() { + return accessUrlPrefix; + } + + public String getPublicBaseUrl() { + return publicBaseUrl; + } + + public void setPublicBaseUrl(String publicBaseUrl) { + this.publicBaseUrl = publicBaseUrl; + } + + public void setAccessUrlPrefix(String accessUrlPrefix) { + this.accessUrlPrefix = accessUrlPrefix; + } + + public Path getUploadPath() { + String dir = StringUtils.hasText(uploadDir) ? uploadDir : "uploads"; + return Path.of(dir).toAbsolutePath().normalize(); + } + + public String getNormalizedAccessUrlPrefix() { + String prefix = StringUtils.hasText(accessUrlPrefix) ? accessUrlPrefix.trim() : "/uploads"; + + if (!prefix.startsWith("/")) { + prefix = "/" + prefix; + } + + while (prefix.length() > 1 && prefix.endsWith("/")) { + prefix = prefix.substring(0, prefix.length() - 1); + } + + return prefix; + } + + public String getResourceHandlerPattern() { + return getNormalizedAccessUrlPrefix() + "/**"; + } + + public String buildAccessUrl(String fileName) { + return getNormalizedPublicBaseUrl() + getNormalizedAccessUrlPrefix() + "/" + fileName; + } + + private String getNormalizedPublicBaseUrl() { + if (!StringUtils.hasText(publicBaseUrl)) { + return ""; + } + + String baseUrl = publicBaseUrl.trim(); + + while (baseUrl.endsWith("/")) { + baseUrl = baseUrl.substring(0, baseUrl.length() - 1); + } + + return baseUrl; + } +} diff --git a/code/ai/ai-springboot/src/main/java/cn/g3soft/config/JacksonConfig.java b/code/ai/ai-springboot/src/main/java/cn/g3soft/config/JacksonConfig.java new file mode 100644 index 00000000..8c8386d2 --- /dev/null +++ b/code/ai/ai-springboot/src/main/java/cn/g3soft/config/JacksonConfig.java @@ -0,0 +1,14 @@ +package cn.g3soft.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class JacksonConfig { + + @Bean + public ObjectMapper objectMapper() { + return new ObjectMapper().findAndRegisterModules(); + } +} diff --git a/code/ai/ai-springboot/src/main/java/cn/g3soft/config/PpStructureProperties.java b/code/ai/ai-springboot/src/main/java/cn/g3soft/config/PpStructureProperties.java new file mode 100644 index 00000000..0d301fad --- /dev/null +++ b/code/ai/ai-springboot/src/main/java/cn/g3soft/config/PpStructureProperties.java @@ -0,0 +1,81 @@ +package cn.g3soft.config; + +import java.net.URI; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +@Component +@ConfigurationProperties(prefix = "app.pp-structure") +public class PpStructureProperties { + + private String baseUrl = "http://192.168.1.137:8866"; + + private String endpointPath = "/layout-parsing"; + + private int requestTimeoutSeconds = 120; + + private int connectTimeoutSeconds = 10; + + public String getBaseUrl() { + return baseUrl; + } + + public void setBaseUrl(String baseUrl) { + this.baseUrl = baseUrl; + } + + public String getEndpointPath() { + return endpointPath; + } + + public void setEndpointPath(String endpointPath) { + this.endpointPath = endpointPath; + } + + public int getRequestTimeoutSeconds() { + return requestTimeoutSeconds; + } + + public void setRequestTimeoutSeconds(int requestTimeoutSeconds) { + this.requestTimeoutSeconds = requestTimeoutSeconds; + } + + public int getConnectTimeoutSeconds() { + return connectTimeoutSeconds; + } + + public void setConnectTimeoutSeconds(int connectTimeoutSeconds) { + this.connectTimeoutSeconds = connectTimeoutSeconds; + } + + public URI getRecognitionUri() { + String normalizedBaseUrl = normalizeBaseUrl(baseUrl); + String normalizedPath = normalizeEndpointPath(endpointPath); + return URI.create(normalizedBaseUrl + normalizedPath); + } + + private String normalizeBaseUrl(String value) { + String normalized = StringUtils.hasText(value) ? value.trim() : "http://192.168.1.137:8866"; + + if (!normalized.startsWith("http://") && !normalized.startsWith("https://")) { + normalized = "http://" + normalized; + } + + while (normalized.endsWith("/")) { + normalized = normalized.substring(0, normalized.length() - 1); + } + + return normalized; + } + + private String normalizeEndpointPath(String value) { + String normalized = StringUtils.hasText(value) ? value.trim() : "/layout-parsing"; + + if (!normalized.startsWith("/")) { + normalized = "/" + normalized; + } + + return normalized; + } +} diff --git a/code/ai/ai-springboot/src/main/java/cn/g3soft/config/WebMvcConfig.java b/code/ai/ai-springboot/src/main/java/cn/g3soft/config/WebMvcConfig.java new file mode 100644 index 00000000..cc6ed7bd --- /dev/null +++ b/code/ai/ai-springboot/src/main/java/cn/g3soft/config/WebMvcConfig.java @@ -0,0 +1,39 @@ +package cn.g3soft.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebMvcConfig implements WebMvcConfigurer { + + private final FileStorageProperties fileStorageProperties; + + public WebMvcConfig(FileStorageProperties fileStorageProperties) { + this.fileStorageProperties = fileStorageProperties; + } + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + String resourceLocation = fileStorageProperties.getUploadPath().toUri().toString(); + + if (!resourceLocation.endsWith("/")) { + resourceLocation += "/"; + } + + registry + .addResourceHandler(fileStorageProperties.getResourceHandlerPattern()) + .addResourceLocations(resourceLocation); + } + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry + .addMapping("/api/**") + .allowedOriginPatterns("*") + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") + .allowedHeaders("*") + .maxAge(3600); + } +} diff --git a/code/ai/ai-springboot/src/main/java/cn/g3soft/file/FileStorageService.java b/code/ai/ai-springboot/src/main/java/cn/g3soft/file/FileStorageService.java new file mode 100644 index 00000000..36cb70c4 --- /dev/null +++ b/code/ai/ai-springboot/src/main/java/cn/g3soft/file/FileStorageService.java @@ -0,0 +1,69 @@ +package cn.g3soft.file; + +import cn.g3soft.config.FileStorageProperties; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Locale; +import java.util.UUID; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.server.ResponseStatusException; + +@Service +public class FileStorageService { + + private final FileStorageProperties fileStorageProperties; + + public FileStorageService(FileStorageProperties fileStorageProperties) { + this.fileStorageProperties = fileStorageProperties; + } + + public FileUploadResponse store(MultipartFile file) { + if (file == null || file.isEmpty()) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "上传文件不能为空"); + } + + String originalName = StringUtils.cleanPath(file.getOriginalFilename() == null ? "" : file.getOriginalFilename()); + String storedFileName = UUID.randomUUID().toString().replace("-", "") + getExtension(originalName); + Path uploadPath = fileStorageProperties.getUploadPath(); + Path targetPath = uploadPath.resolve(storedFileName).normalize(); + + if (!targetPath.startsWith(uploadPath)) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "文件名不合法"); + } + + try { + Files.createDirectories(uploadPath); + file.transferTo(targetPath); + } catch (IOException ex) { + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "文件保存失败", ex); + } + + return new FileUploadResponse( + originalName, + storedFileName, + fileStorageProperties.buildAccessUrl(storedFileName), + file.getSize(), + file.getContentType() + ); + } + + private String getExtension(String originalName) { + if (!StringUtils.hasText(originalName)) { + return ""; + } + + int slashIndex = Math.max(originalName.lastIndexOf('/'), originalName.lastIndexOf('\\')); + String fileName = originalName.substring(slashIndex + 1); + int dotIndex = fileName.lastIndexOf('.'); + + if (dotIndex < 0 || dotIndex == fileName.length() - 1) { + return ""; + } + + return fileName.substring(dotIndex).toLowerCase(Locale.ROOT); + } +} diff --git a/code/ai/ai-springboot/src/main/java/cn/g3soft/file/FileUploadController.java b/code/ai/ai-springboot/src/main/java/cn/g3soft/file/FileUploadController.java new file mode 100644 index 00000000..bec23539 --- /dev/null +++ b/code/ai/ai-springboot/src/main/java/cn/g3soft/file/FileUploadController.java @@ -0,0 +1,23 @@ +package cn.g3soft.file; + +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +@RestController +@RequestMapping("/api/files") +public class FileUploadController { + + private final FileStorageService fileStorageService; + + public FileUploadController(FileStorageService fileStorageService) { + this.fileStorageService = fileStorageService; + } + + @PostMapping("/upload") + public FileUploadResponse upload(@RequestParam("file") MultipartFile file) { + return fileStorageService.store(file); + } +} diff --git a/code/ai/ai-springboot/src/main/java/cn/g3soft/file/FileUploadResponse.java b/code/ai/ai-springboot/src/main/java/cn/g3soft/file/FileUploadResponse.java new file mode 100644 index 00000000..112011e8 --- /dev/null +++ b/code/ai/ai-springboot/src/main/java/cn/g3soft/file/FileUploadResponse.java @@ -0,0 +1,10 @@ +package cn.g3soft.file; + +public record FileUploadResponse( + String originalName, + String fileName, + String url, + long size, + String contentType +) { +} diff --git a/code/ai/ai-springboot/src/main/java/cn/g3soft/recognition/RecognitionController.java b/code/ai/ai-springboot/src/main/java/cn/g3soft/recognition/RecognitionController.java new file mode 100644 index 00000000..8a8717f9 --- /dev/null +++ b/code/ai/ai-springboot/src/main/java/cn/g3soft/recognition/RecognitionController.java @@ -0,0 +1,22 @@ +package cn.g3soft.recognition; + +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/recognitions") +public class RecognitionController { + + private final RecognitionService recognitionService; + + public RecognitionController(RecognitionService recognitionService) { + this.recognitionService = recognitionService; + } + + @PostMapping("/start") + public RecognitionResponse start(@RequestBody RecognitionRequest request) { + return recognitionService.recognize(request); + } +} diff --git a/code/ai/ai-springboot/src/main/java/cn/g3soft/recognition/RecognitionRequest.java b/code/ai/ai-springboot/src/main/java/cn/g3soft/recognition/RecognitionRequest.java new file mode 100644 index 00000000..5cb831f8 --- /dev/null +++ b/code/ai/ai-springboot/src/main/java/cn/g3soft/recognition/RecognitionRequest.java @@ -0,0 +1,7 @@ +package cn.g3soft.recognition; + +public record RecognitionRequest( + String url, + String type +) { +} diff --git a/code/ai/ai-springboot/src/main/java/cn/g3soft/recognition/RecognitionResponse.java b/code/ai/ai-springboot/src/main/java/cn/g3soft/recognition/RecognitionResponse.java new file mode 100644 index 00000000..56bdbd2c --- /dev/null +++ b/code/ai/ai-springboot/src/main/java/cn/g3soft/recognition/RecognitionResponse.java @@ -0,0 +1,12 @@ +package cn.g3soft.recognition; + +import com.fasterxml.jackson.databind.JsonNode; + +public record RecognitionResponse( + String url, + String type, + String resultFileName, + String resultFileUrl, + JsonNode ppStructureResult +) { +} diff --git a/code/ai/ai-springboot/src/main/java/cn/g3soft/recognition/RecognitionService.java b/code/ai/ai-springboot/src/main/java/cn/g3soft/recognition/RecognitionService.java new file mode 100644 index 00000000..8c93e7a5 --- /dev/null +++ b/code/ai/ai-springboot/src/main/java/cn/g3soft/recognition/RecognitionService.java @@ -0,0 +1,146 @@ +package cn.g3soft.recognition; + +import cn.g3soft.config.PpStructureProperties; +import cn.g3soft.config.FileStorageProperties; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.net.http.HttpConnectTimeoutException; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.net.http.HttpTimeoutException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Duration; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.UUID; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; +import org.springframework.web.server.ResponseStatusException; + +@Service +public class RecognitionService { + + private static final Logger log = LoggerFactory.getLogger(RecognitionService.class); + + private final PpStructureProperties ppStructureProperties; + + private final FileStorageProperties fileStorageProperties; + + private final ObjectMapper objectMapper; + + public RecognitionService(PpStructureProperties ppStructureProperties, FileStorageProperties fileStorageProperties, ObjectMapper objectMapper) { + this.ppStructureProperties = ppStructureProperties; + this.fileStorageProperties = fileStorageProperties; + this.objectMapper = objectMapper; + } + + public RecognitionResponse recognize(RecognitionRequest recognitionRequest) { + validateRequest(recognitionRequest); + + JsonNode ppStructureResult = callPpStructure(recognitionRequest); + String resultText = ppStructureResult.toPrettyString(); + String resultFileName = saveRecognitionResult(resultText); + log.info("PP-StructureV3 recognition result, type={}, url={}, result={}", + recognitionRequest.type(), + recognitionRequest.url(), + resultText); + log.info("PP-StructureV3 recognition result txt saved, fileName={}, url={}", + resultFileName, + fileStorageProperties.buildAccessUrl(resultFileName)); + + return new RecognitionResponse( + recognitionRequest.url(), + recognitionRequest.type(), + resultFileName, + fileStorageProperties.buildAccessUrl(resultFileName), + ppStructureResult + ); + } + + private JsonNode callPpStructure(RecognitionRequest recognitionRequest) { + Map ppStructureRequest = new LinkedHashMap<>(); + ppStructureRequest.put("file", recognitionRequest.url()); + + try { + String requestBody = objectMapper.writeValueAsString(ppStructureRequest); + log.info("PP-StructureV3 request uri={}", ppStructureProperties.getRecognitionUri()); + log.info("PP-StructureV3 request body={}", requestBody); + + HttpRequest request = HttpRequest + .newBuilder(ppStructureProperties.getRecognitionUri()) + .timeout(Duration.ofSeconds(ppStructureProperties.getRequestTimeoutSeconds())) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofString(requestBody)) + .build(); + + HttpClient client = HttpClient + .newBuilder() + .connectTimeout(Duration.ofSeconds(ppStructureProperties.getConnectTimeoutSeconds())) + .build(); + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + String responseBody = response.body(); + + log.info("PP-StructureV3 response status={}", response.statusCode()); + log.info("PP-StructureV3 response headers={}", response.headers().map()); + log.info("PP-StructureV3 response body={}", responseBody); + + if (response.statusCode() < 200 || response.statusCode() >= 300) { + throw new ResponseStatusException( + HttpStatus.BAD_GATEWAY, + "PP-StructureV3识别失败,状态码:" + response.statusCode() + ",响应:" + responseBody + ); + } + + return objectMapper.readTree(responseBody); + } catch (HttpConnectTimeoutException ex) { + log.error("PP-StructureV3 connect timeout, uri={}", ppStructureProperties.getRecognitionUri(), ex); + throw new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT, "PP-StructureV3连接超时", ex); + } catch (HttpTimeoutException ex) { + log.error("PP-StructureV3 request timeout, uri={}", ppStructureProperties.getRecognitionUri(), ex); + throw new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT, "PP-StructureV3识别请求超时", ex); + } catch (IOException ex) { + log.error("PP-StructureV3 request or response parse failed, uri={}", ppStructureProperties.getRecognitionUri(), ex); + throw new ResponseStatusException(HttpStatus.BAD_GATEWAY, "PP-StructureV3识别响应解析失败", ex); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + log.error("PP-StructureV3 request interrupted, uri={}", ppStructureProperties.getRecognitionUri(), ex); + throw new ResponseStatusException(HttpStatus.BAD_GATEWAY, "PP-StructureV3识别请求被中断", ex); + } + } + + private void validateRequest(RecognitionRequest request) { + if (request == null || !StringUtils.hasText(request.url())) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "url不能为空"); + } + + if (!StringUtils.hasText(request.type())) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "type不能为空"); + } + } + + private String saveRecognitionResult(String resultText) { + String resultFileName = UUID.randomUUID().toString().replace("-", "") + ".txt"; + Path uploadPath = fileStorageProperties.getUploadPath(); + Path targetPath = uploadPath.resolve(resultFileName).normalize(); + + if (!targetPath.startsWith(uploadPath)) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "识别结果文件名不合法"); + } + + try { + Files.createDirectories(uploadPath); + Files.writeString(targetPath, resultText, StandardCharsets.UTF_8); + return resultFileName; + } catch (IOException ex) { + log.error("Save PP-StructureV3 recognition result txt failed, path={}", targetPath, ex); + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "识别结果文件保存失败", ex); + } + } +} diff --git a/code/ai/ai-springboot/src/main/resources/application.yml b/code/ai/ai-springboot/src/main/resources/application.yml new file mode 100644 index 00000000..ee9985d1 --- /dev/null +++ b/code/ai/ai-springboot/src/main/resources/application.yml @@ -0,0 +1,20 @@ +server: + port: 8082 + +app: + file: + upload-dir: D:/data/test + public-base-url: http://192.168.1.137:8082 + access-url-prefix: /uploads + pp-structure: + base-url: http://192.168.1.137:8866 + endpoint-path: /layout-parsing + request-timeout-seconds: 15 + connect-timeout-seconds: 15 + +spring: + ai: + openai: + api-key: xxx + base-url: https://api.openai.com + model: xxx diff --git a/code/ai/ai-springboot/src/test/java/cn/g3soft/ai/AiSpringbootApplicationTests.java b/code/ai/ai-springboot/src/test/java/cn/g3soft/ai/AiSpringbootApplicationTests.java new file mode 100644 index 00000000..8aa8bd13 --- /dev/null +++ b/code/ai/ai-springboot/src/test/java/cn/g3soft/ai/AiSpringbootApplicationTests.java @@ -0,0 +1,13 @@ +package cn.g3soft.ai; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class AiSpringbootApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/code/ai/ai-vue/.env.development b/code/ai/ai-vue/.env.development new file mode 100644 index 00000000..a3d0c3ff --- /dev/null +++ b/code/ai/ai-vue/.env.development @@ -0,0 +1 @@ +VITE_API_BASE_URL=http://192.168.1.137:8082 diff --git a/code/ai/ai-vue/.gitignore b/code/ai/ai-vue/.gitignore new file mode 100644 index 00000000..a547bf36 --- /dev/null +++ b/code/ai/ai-vue/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/code/ai/ai-vue/.oxfmtrc.json b/code/ai/ai-vue/.oxfmtrc.json new file mode 100644 index 00000000..55c15df3 --- /dev/null +++ b/code/ai/ai-vue/.oxfmtrc.json @@ -0,0 +1,4 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "ignorePatterns": [] +} diff --git a/code/ai/ai-vue/.vscode/extensions.json b/code/ai/ai-vue/.vscode/extensions.json new file mode 100644 index 00000000..a7cea0b0 --- /dev/null +++ b/code/ai/ai-vue/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["Vue.volar"] +} diff --git a/code/ai/ai-vue/README.md b/code/ai/ai-vue/README.md new file mode 100644 index 00000000..33895ab2 --- /dev/null +++ b/code/ai/ai-vue/README.md @@ -0,0 +1,5 @@ +# Vue 3 + TypeScript + Vite + +This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 ` + + diff --git a/code/ai/ai-vue/package.json b/code/ai/ai-vue/package.json new file mode 100644 index 00000000..778a3ca2 --- /dev/null +++ b/code/ai/ai-vue/package.json @@ -0,0 +1,27 @@ +{ + "name": "ai-vue", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc -b && vite build", + "preview": "vite preview", + "fmt": "oxfmt", + "fmt:check": "oxfmt --check" + }, + "dependencies": { + "alova": "^3.5.1", + "naive-ui": "^2.44.1", + "vue": "^3.5.34" + }, + "devDependencies": { + "@types/node": "^24.12.3", + "@vitejs/plugin-vue": "^6.0.6", + "@vue/tsconfig": "^0.9.1", + "oxfmt": "^0.54.0", + "typescript": "~6.0.2", + "vite": "^8.0.12", + "vue-tsc": "^3.2.8" + } +} diff --git a/code/ai/ai-vue/pnpm-lock.yaml b/code/ai/ai-vue/pnpm-lock.yaml new file mode 100644 index 00000000..5cbe8be7 --- /dev/null +++ b/code/ai/ai-vue/pnpm-lock.yaml @@ -0,0 +1,1205 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + alova: + specifier: ^3.5.1 + version: 3.5.1 + naive-ui: + specifier: ^2.44.1 + version: 2.44.1(vue@3.5.35(typescript@6.0.3)) + vue: + specifier: ^3.5.34 + version: 3.5.35(typescript@6.0.3) + devDependencies: + '@types/node': + specifier: ^24.12.3 + version: 24.13.1 + '@vitejs/plugin-vue': + specifier: ^6.0.6 + version: 6.0.7(vite@8.0.16(@types/node@24.13.1))(vue@3.5.35(typescript@6.0.3)) + '@vue/tsconfig': + specifier: ^0.9.1 + version: 0.9.1(typescript@6.0.3)(vue@3.5.35(typescript@6.0.3)) + oxfmt: + specifier: ^0.54.0 + version: 0.54.0 + typescript: + specifier: ~6.0.2 + version: 6.0.3 + vite: + specifier: ^8.0.12 + version: 8.0.16(@types/node@24.13.1) + vue-tsc: + specifier: ^3.2.8 + version: 3.3.4(typescript@6.0.3) + +packages: + + '@alova/shared@1.3.2': + resolution: {integrity: sha512-1XvDLWgYpVZ99MmLl1f3Fw4T6S6pPYk5afz5cwRVjuq8JXEGsDn9IygDKfvRyWqkqCBx7Jif07LIct1O+MVEow==} + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} + engines: {node: '>=6.9.0'} + + '@css-render/plugin-bem@0.15.14': + resolution: {integrity: sha512-QK513CJ7yEQxm/P3EwsI+d+ha8kSOcjGvD6SevM41neEMxdULE+18iuQK6tEChAWMOQNQPLG/Rw3Khb69r5neg==} + peerDependencies: + css-render: ~0.15.14 + + '@css-render/vue3-ssr@0.15.14': + resolution: {integrity: sha512-//8027GSbxE9n3QlD73xFY6z4ZbHbvrOVB7AO6hsmrEzGbg+h2A09HboUyDgu+xsmj7JnvJD39Irt+2D0+iV8g==} + peerDependencies: + vue: ^3.0.11 + + '@emnapi/core@1.10.0': + resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} + + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + + '@emnapi/wasi-threads@1.2.1': + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + + '@emotion/hash@0.8.0': + resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@juggle/resize-observer@3.4.0': + resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==} + + '@napi-rs/wasm-runtime@1.1.5': + resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 + + '@oxc-project/types@0.133.0': + resolution: {integrity: sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==} + + '@oxfmt/binding-android-arm-eabi@0.54.0': + resolution: {integrity: sha512-NAtpl/SiaeU103e7/OmZw0MvUnsUUopW7hEm/ecegJg7YM0skQaA0IXEZoyTV6NUdiNPupdIUreRqUZTShbn/g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxfmt/binding-android-arm64@0.54.0': + resolution: {integrity: sha512-B4VZfBUlKK1rmMChsssNZbkZjE8+FzG3avMjGgMDwbGxXRoXkoeXiAZ+78Oa+eyDPHvDCiUb4zH/vmCOUSafLQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxfmt/binding-darwin-arm64@0.54.0': + resolution: {integrity: sha512-i02vF75b+ePsQP3tHqSxVYI5S6b8X/xqdPu7/mDHXtpgXLTYXi3jJmfHU0j+dnZZDKaYTx/ioCK7QYJmtiJR2g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxfmt/binding-darwin-x64@0.54.0': + resolution: {integrity: sha512-8VMFvGvooXj7mswkbrhdVZ2/sgiDaBzWpkkbtO+qGDLV4EfJd67nQadHkQC0ZNbaWA9ajXfqI6i7PZLIeDzxEQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxfmt/binding-freebsd-x64@0.54.0': + resolution: {integrity: sha512-0cRHnp43WN1Jrc5s0BdbdKgR1XirdvHy7TAFi3JEsoEVQVJxTXMbpVd76sxXlgRswNMDhVFSJw+y7Eb8mEavFQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxfmt/binding-linux-arm-gnueabihf@0.54.0': + resolution: {integrity: sha512-JyQAk3hK/OEtup7Rw6kZwfdzbKqTVD5jXXb8Xpfay29suwZyfBDMVW/bj4RqEPySYWc6zCp198pOluf8n5uYzg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm-musleabihf@0.54.0': + resolution: {integrity: sha512-qnvLatTpM8vtvjOfcckBOzJjk+n6ce/wwpP8OFeUrD5aNLYcKyWAitwj+Rk3PK9jGanbZvKsJnv14JGQ6XqFdw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm64-gnu@0.54.0': + resolution: {integrity: sha512-SMkhnCzIYZYDk9vw3W/80eeYKmrMpGF0Giuxt4HruFlCH7jEtnPeb3SdQKMfgYi/dgtaf+hZAb5XWPYnxqCQ3w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-arm64-musl@0.54.0': + resolution: {integrity: sha512-QrwJlBFFKnxOd95TAaszpMbZBLzMoYMpGaQTZF8oibacnF5rv8l12IhILhQRPmksWiBqg0YSe2Mnl7ayeJAHSA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-linux-ppc64-gnu@0.54.0': + resolution: {integrity: sha512-WILatiol/TUHTlhod7R09+7Az/XlhKwmY1MHfLZNmewltPWNN/EwxP2rQSHahibZ/cB8gmckEBjBOByD+5bYsQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-riscv64-gnu@0.54.0': + resolution: {integrity: sha512-f05YMG4BH4G8S4ME6UM6fi1MnJ9094mrnvO5Pa4SJlMfWlUM+1/ZWMEF4NnjM7shZAvbHsHRuVYpUo0PHC4P9Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-riscv64-musl@0.54.0': + resolution: {integrity: sha512-UfL+2hj1ClNqcCRT9s8vBU4axDpjxgVxX96G+9DYAYjoc5b0u15CJtn2jgsi9iM+EbGNc5CW1HVRgwVu76UsSA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-linux-s390x-gnu@0.54.0': + resolution: {integrity: sha512-3/XZe931Hka+J6NjnaqJzYpsWWxDTuRdUdwSQHnOuJEgbC+SehIMFJS8hsEjV7LBhVSL2OCnRLvbVW8O97XIyw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-x64-gnu@0.54.0': + resolution: {integrity: sha512-Ik93RlObtu43GbxApafayFjwYE06L6Xr08cSwpBPYbDrLp2ReZx0Jm1DqwRyYRnukUJy+rK2WaEvUQOxdytU9Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-x64-musl@0.54.0': + resolution: {integrity: sha512-yZcakmPlD86CNymknd7KfW+FH+qfbqJH+i0h69CYfV1+KMoVeM9UED+8+TDVoU4haxI0NxY7RPCvRLy3Sqd2Qg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-openharmony-arm64@0.54.0': + resolution: {integrity: sha512-GiVBZNnEZnKu00f1jTg49nomv187d0GQX+O+ocykoLeiaALuEO+swoTehHn9TehTfi7V8H0i0e/yvUjCqnwk1w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxfmt/binding-win32-arm64-msvc@0.54.0': + resolution: {integrity: sha512-J0SSB8Z1Fre2sxRolYcW6Rl1RQmKdQ2hnHyq4YJrfBRiXTObLw4DXnIVraM/UyqGqwOi7yTrQA4VT7DPxlHVKA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxfmt/binding-win32-ia32-msvc@0.54.0': + resolution: {integrity: sha512-O61UDVj8zz6yXJjkHPf05VaMLOXmEF8P5kf/N0W7AQMmd6bcQogl+KJc7rMutKTL524oE9iH32JXZClBFmEQIg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxfmt/binding-win32-x64-msvc@0.54.0': + resolution: {integrity: sha512-1MDpqJPiFqxWtIHas8vkb1VZ7f7eKyTffAwmO8isxQYMaG1OFKsH666BWLeXQLO+IWNfiMssLD55hbR1lIPTqg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/binding-android-arm64@1.0.3': + resolution: {integrity: sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.0.3': + resolution: {integrity: sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.0.3': + resolution: {integrity: sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.0.3': + resolution: {integrity: sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.0.3': + resolution: {integrity: sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.0.3': + resolution: {integrity: sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.0.3': + resolution: {integrity: sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.0.3': + resolution: {integrity: sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.0.3': + resolution: {integrity: sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.0.3': + resolution: {integrity: sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.0.3': + resolution: {integrity: sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.0.3': + resolution: {integrity: sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.0.3': + resolution: {integrity: sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.0.3': + resolution: {integrity: sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.0.3': + resolution: {integrity: sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@tybys/wasm-util@0.10.2': + resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} + + '@types/lodash-es@4.17.12': + resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} + + '@types/lodash@4.17.24': + resolution: {integrity: sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==} + + '@types/node@24.13.1': + resolution: {integrity: sha512-RSpUJGmvsJ1ZeBehQZFhIdpsz+bIpES0nIQXko4Ybq+N+kX6XvOq3Jo+iJ82FWLdblFq85AsMikd3m35jgezYg==} + + '@vitejs/plugin-vue@6.0.7': + resolution: {integrity: sha512-km+p+XdSz9Sxm5rqUbqcSfZYaAniKxWBj1KURl+Jr7UaPvvX7BmaWMdP69I5rrFDeQGyxAG7NXdc57vz+snhWg==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + vue: ^3.2.25 + + '@volar/language-core@2.4.28': + resolution: {integrity: sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==} + + '@volar/source-map@2.4.28': + resolution: {integrity: sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==} + + '@volar/typescript@2.4.28': + resolution: {integrity: sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==} + + '@vue/compiler-core@3.5.35': + resolution: {integrity: sha512-BUmHaR1J+O+CKZ9uJucdVTEr1LHsdyvv7vG3eNRhK3CczEHeMd/LtsHAuD7PbrxvI2envCY2v7HI1vC1aBRzKw==} + + '@vue/compiler-dom@3.5.35': + resolution: {integrity: sha512-k+bprkXxuqhVajgTx5mUHuir7TwQzUKOWR40ng1ncAqQRPnrLngGGgqVEEhOnTMlc8btHYVKmrP8s5Qyg0hvYA==} + + '@vue/compiler-sfc@3.5.35': + resolution: {integrity: sha512-G5VPMcXTSywXBgtFOZOnHKBxKSrwXUcvY1iaF5/hRcy7t0J6CH/d8ha9F4nzi00Fax1eLV0QHM7v4mQu68jydw==} + + '@vue/compiler-ssr@3.5.35': + resolution: {integrity: sha512-rGhAeXgdM7/ffTJGXT69rCCdTmjDewnFuUZfBQQHTdcEBeWdT5HCGY60y2ytLJr9/Dsu7IntUi5z/w0h6Rjnzw==} + + '@vue/language-core@3.3.4': + resolution: {integrity: sha512-IuHqQ5zGGOE7CXP72VX6A42IVeIzYv4WAhO6arej11TRNqtdZfGyH8Yr2FOCaDX0dSQG+JwULLoFHGY1igYVjQ==} + + '@vue/reactivity@3.5.35': + resolution: {integrity: sha512-tVc+SsHConvh/Lz64qq1pP3rYArBmK42xonovEcxY74SQtvctZodG/zhq54P5dr38cVuw25d27cPNRdlMidpGQ==} + + '@vue/runtime-core@3.5.35': + resolution: {integrity: sha512-A/xFNX9loIcWDygeQuNCfKuh0CoYBzxhqEMNah5TSFg9Z53DrFYEN2qi5CU9necjM1OWYegYREUTHmXTmhfXtg==} + + '@vue/runtime-dom@3.5.35': + resolution: {integrity: sha512-odrJ1C391dbGnyDRh8U+rnP7J2amIEzfmRk5vXy7xi3aZhEXofTvpi0T4HJb6jlNqQZTNPR5MPHSB3RHNkIORA==} + + '@vue/server-renderer@3.5.35': + resolution: {integrity: sha512-NkebSOYdB97wi8OQcO3HqzZSlymJi/aWsN/7h74OSVhRTm6qGs3Jp3e0rCXynmWwSlKeRrnlIug+ilYoHBmQDA==} + peerDependencies: + vue: 3.5.35 + + '@vue/shared@3.5.35': + resolution: {integrity: sha512-zSbjL7gRXwks2ZQLRGCajBtBXEOXW9Ddhn/HvSdrGkE2dqGnumzW8XtusRrxrE9LvqtiqDXQ+A60Hp6mvdYxfA==} + + '@vue/tsconfig@0.9.1': + resolution: {integrity: sha512-buvjm+9NzLCJL29KY1j1991YYJ5e6275OiK+G4jtmfIb+z4POywbdm0wXusT9adVWqe0xqg70TbI7+mRx4uU9w==} + peerDependencies: + typescript: '>= 5.8' + vue: ^3.4.0 + peerDependenciesMeta: + typescript: + optional: true + vue: + optional: true + + alien-signals@3.2.1: + resolution: {integrity: sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==} + + alova@3.5.1: + resolution: {integrity: sha512-avrWPyFFWW51YLoy0S3OleNw1BV0GqNI+DSdWHfFbAoKZp80cXCCc7OtjA6OWeyhCOMglUMwo9O8j5huwnzFtQ==} + engines: {node: '>= 18.0.0'} + + async-validator@4.2.5: + resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==} + + css-render@0.15.14: + resolution: {integrity: sha512-9nF4PdUle+5ta4W5SyZdLCCmFd37uVimSjg1evcTqKJCyvCEEj12WKzOSBNak6r4im4J4iYXKH1OWpUV5LBYFg==} + + csstype@3.0.11: + resolution: {integrity: sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + date-fns-tz@3.2.0: + resolution: {integrity: sha512-sg8HqoTEulcbbbVXeg84u5UnlsQa8GS5QXMqjjYIhS4abEVVKIUwe0/l/UhrZdKaL/W5eWZNlbTeEIiOXTcsBQ==} + peerDependencies: + date-fns: ^3.0.0 || ^4.0.0 + + date-fns@4.4.0: + resolution: {integrity: sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + evtd@0.2.4: + resolution: {integrity: sha512-qaeGN5bx63s/AXgQo8gj6fBkxge+OoLddLniox5qtLAEY5HSnuSlISXVPxnSae1dWblvTh4/HoMIB+mbMsvZzw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + highlight.js@11.11.1: + resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} + engines: {node: '>=12.0.0'} + + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + + lodash-es@4.18.1: + resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} + + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + muggle-string@0.4.1: + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} + + naive-ui@2.44.1: + resolution: {integrity: sha512-reo8Esw0p58liZwbUutC7meW24Xbn3EwNv91zReWKm2W4JPu+zfgJRn/F7aO0BFmvN+h2brA2M5lRvYqLq4kuA==} + engines: {node: '>=20'} + peerDependencies: + vue: ^3.0.0 + + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + oxfmt@0.54.0: + resolution: {integrity: sha512-DjnMwn7smSLF+Mc2+pRItnuPftm/dkUFpY/d4+33y9TfKrsHZo8GLhmUg9BrOIUEy94Rlom1Q11N6vuhE+e0oQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + svelte: ^5.0.0 + vite-plus: '*' + peerDependenciesMeta: + svelte: + optional: true + vite-plus: + optional: true + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + postcss@8.5.15: + resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} + engines: {node: ^10 || ^12 || >=14} + + rate-limiter-flexible@5.0.5: + resolution: {integrity: sha512-+/dSQfo+3FYwYygUs/V2BBdwGa9nFtakDwKt4l0bnvNB53TNT++QSFewwHX9qXrZJuMe9j+TUaU21lm5ARgqdQ==} + + rolldown@1.0.3: + resolution: {integrity: sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + seemly@0.3.10: + resolution: {integrity: sha512-2+SMxtG1PcsL0uyhkumlOU6Qo9TAQ/WyH7tthnPIOQB05/12jz9naq6GZ6iZ6ApVsO3rr2gsnTf3++OV63kE1Q==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + tinypool@2.1.0: + resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==} + engines: {node: ^20.0.0 || >=22.0.0} + + treemate@0.3.11: + resolution: {integrity: sha512-M8RGFoKtZ8dF+iwJfAJTOH/SM4KluKOKRJpjCMhI8bG3qB74zrFoArKZ62ll0Fr3mqkMJiQOmWYkdYgDeITYQg==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@7.18.2: + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + + vdirs@0.1.8: + resolution: {integrity: sha512-H9V1zGRLQZg9b+GdMk8MXDN2Lva0zx72MPahDKc30v+DtwKjfyOSXWRIX4t2mhDubM1H09gPhWeth/BJWPHGUw==} + peerDependencies: + vue: ^3.0.11 + + vite@8.0.16: + resolution: {integrity: sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.1.18 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vooks@0.2.12: + resolution: {integrity: sha512-iox0I3RZzxtKlcgYaStQYKEzWWGAduMmq+jS7OrNdQo1FgGfPMubGL3uGHOU9n97NIvfFDBGnpSvkWyb/NSn/Q==} + peerDependencies: + vue: ^3.0.0 + + vscode-uri@3.1.0: + resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} + + vue-tsc@3.3.4: + resolution: {integrity: sha512-XA/JqmQwS2GZmfgpjOEGdrKwaTSEuPwxpHa7/t6f4yiGrJb3gVHTPb9wBfByMNZwQ+xDXs41b8gaS2DKsOozUw==} + hasBin: true + peerDependencies: + typescript: '>=5.0.0' + + vue@3.5.35: + resolution: {integrity: sha512-cx89fnr+0kVGHiNFG6y6s0bdjypJRFNZn6x3WPstNdQR1bi1mbB7h4v5IBGTsPJU3nK1+0Iqj3Zf+hZWMieR4Q==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + vueuc@0.4.65: + resolution: {integrity: sha512-lXuMl+8gsBmruudfxnMF9HW4be8rFziylXFu1VHVNbLVhRTXXV4njvpRuJapD/8q+oFEMSfQMH16E/85VoWRyQ==} + peerDependencies: + vue: ^3.0.11 + +snapshots: + + '@alova/shared@1.3.2': {} + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/parser@7.29.7': + dependencies: + '@babel/types': 7.29.7 + + '@babel/types@7.29.7': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@css-render/plugin-bem@0.15.14(css-render@0.15.14)': + dependencies: + css-render: 0.15.14 + + '@css-render/vue3-ssr@0.15.14(vue@3.5.35(typescript@6.0.3))': + dependencies: + vue: 3.5.35(typescript@6.0.3) + + '@emnapi/core@1.10.0': + dependencies: + '@emnapi/wasi-threads': 1.2.1 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.10.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@emotion/hash@0.8.0': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@juggle/resize-observer@3.4.0': {} + + '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@tybys/wasm-util': 0.10.2 + optional: true + + '@oxc-project/types@0.133.0': {} + + '@oxfmt/binding-android-arm-eabi@0.54.0': + optional: true + + '@oxfmt/binding-android-arm64@0.54.0': + optional: true + + '@oxfmt/binding-darwin-arm64@0.54.0': + optional: true + + '@oxfmt/binding-darwin-x64@0.54.0': + optional: true + + '@oxfmt/binding-freebsd-x64@0.54.0': + optional: true + + '@oxfmt/binding-linux-arm-gnueabihf@0.54.0': + optional: true + + '@oxfmt/binding-linux-arm-musleabihf@0.54.0': + optional: true + + '@oxfmt/binding-linux-arm64-gnu@0.54.0': + optional: true + + '@oxfmt/binding-linux-arm64-musl@0.54.0': + optional: true + + '@oxfmt/binding-linux-ppc64-gnu@0.54.0': + optional: true + + '@oxfmt/binding-linux-riscv64-gnu@0.54.0': + optional: true + + '@oxfmt/binding-linux-riscv64-musl@0.54.0': + optional: true + + '@oxfmt/binding-linux-s390x-gnu@0.54.0': + optional: true + + '@oxfmt/binding-linux-x64-gnu@0.54.0': + optional: true + + '@oxfmt/binding-linux-x64-musl@0.54.0': + optional: true + + '@oxfmt/binding-openharmony-arm64@0.54.0': + optional: true + + '@oxfmt/binding-win32-arm64-msvc@0.54.0': + optional: true + + '@oxfmt/binding-win32-ia32-msvc@0.54.0': + optional: true + + '@oxfmt/binding-win32-x64-msvc@0.54.0': + optional: true + + '@rolldown/binding-android-arm64@1.0.3': + optional: true + + '@rolldown/binding-darwin-arm64@1.0.3': + optional: true + + '@rolldown/binding-darwin-x64@1.0.3': + optional: true + + '@rolldown/binding-freebsd-x64@1.0.3': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.0.3': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.0.3': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.0.3': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.0.3': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.0.3': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.0.3': + optional: true + + '@rolldown/binding-linux-x64-musl@1.0.3': + optional: true + + '@rolldown/binding-openharmony-arm64@1.0.3': + optional: true + + '@rolldown/binding-wasm32-wasi@1.0.3': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.0.3': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.0.3': + optional: true + + '@rolldown/pluginutils@1.0.1': {} + + '@tybys/wasm-util@0.10.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/lodash-es@4.17.12': + dependencies: + '@types/lodash': 4.17.24 + + '@types/lodash@4.17.24': {} + + '@types/node@24.13.1': + dependencies: + undici-types: 7.18.2 + + '@vitejs/plugin-vue@6.0.7(vite@8.0.16(@types/node@24.13.1))(vue@3.5.35(typescript@6.0.3))': + dependencies: + '@rolldown/pluginutils': 1.0.1 + vite: 8.0.16(@types/node@24.13.1) + vue: 3.5.35(typescript@6.0.3) + + '@volar/language-core@2.4.28': + dependencies: + '@volar/source-map': 2.4.28 + + '@volar/source-map@2.4.28': {} + + '@volar/typescript@2.4.28': + dependencies: + '@volar/language-core': 2.4.28 + path-browserify: 1.0.1 + vscode-uri: 3.1.0 + + '@vue/compiler-core@3.5.35': + dependencies: + '@babel/parser': 7.29.7 + '@vue/shared': 3.5.35 + entities: 7.0.1 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.35': + dependencies: + '@vue/compiler-core': 3.5.35 + '@vue/shared': 3.5.35 + + '@vue/compiler-sfc@3.5.35': + dependencies: + '@babel/parser': 7.29.7 + '@vue/compiler-core': 3.5.35 + '@vue/compiler-dom': 3.5.35 + '@vue/compiler-ssr': 3.5.35 + '@vue/shared': 3.5.35 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.15 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.35': + dependencies: + '@vue/compiler-dom': 3.5.35 + '@vue/shared': 3.5.35 + + '@vue/language-core@3.3.4': + dependencies: + '@volar/language-core': 2.4.28 + '@vue/compiler-dom': 3.5.35 + '@vue/shared': 3.5.35 + alien-signals: 3.2.1 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + picomatch: 4.0.4 + + '@vue/reactivity@3.5.35': + dependencies: + '@vue/shared': 3.5.35 + + '@vue/runtime-core@3.5.35': + dependencies: + '@vue/reactivity': 3.5.35 + '@vue/shared': 3.5.35 + + '@vue/runtime-dom@3.5.35': + dependencies: + '@vue/reactivity': 3.5.35 + '@vue/runtime-core': 3.5.35 + '@vue/shared': 3.5.35 + csstype: 3.2.3 + + '@vue/server-renderer@3.5.35(vue@3.5.35(typescript@6.0.3))': + dependencies: + '@vue/compiler-ssr': 3.5.35 + '@vue/shared': 3.5.35 + vue: 3.5.35(typescript@6.0.3) + + '@vue/shared@3.5.35': {} + + '@vue/tsconfig@0.9.1(typescript@6.0.3)(vue@3.5.35(typescript@6.0.3))': + optionalDependencies: + typescript: 6.0.3 + vue: 3.5.35(typescript@6.0.3) + + alien-signals@3.2.1: {} + + alova@3.5.1: + dependencies: + '@alova/shared': 1.3.2 + rate-limiter-flexible: 5.0.5 + + async-validator@4.2.5: {} + + css-render@0.15.14: + dependencies: + '@emotion/hash': 0.8.0 + csstype: 3.0.11 + + csstype@3.0.11: {} + + csstype@3.2.3: {} + + date-fns-tz@3.2.0(date-fns@4.4.0): + dependencies: + date-fns: 4.4.0 + + date-fns@4.4.0: {} + + detect-libc@2.1.2: {} + + entities@7.0.1: {} + + estree-walker@2.0.2: {} + + evtd@0.2.4: {} + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + fsevents@2.3.3: + optional: true + + highlight.js@11.11.1: {} + + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + + lodash-es@4.18.1: {} + + lodash@4.18.1: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + muggle-string@0.4.1: {} + + naive-ui@2.44.1(vue@3.5.35(typescript@6.0.3)): + dependencies: + '@css-render/plugin-bem': 0.15.14(css-render@0.15.14) + '@css-render/vue3-ssr': 0.15.14(vue@3.5.35(typescript@6.0.3)) + '@types/lodash': 4.17.24 + '@types/lodash-es': 4.17.12 + async-validator: 4.2.5 + css-render: 0.15.14 + csstype: 3.2.3 + date-fns: 4.4.0 + date-fns-tz: 3.2.0(date-fns@4.4.0) + evtd: 0.2.4 + highlight.js: 11.11.1 + lodash: 4.18.1 + lodash-es: 4.18.1 + seemly: 0.3.10 + treemate: 0.3.11 + vdirs: 0.1.8(vue@3.5.35(typescript@6.0.3)) + vooks: 0.2.12(vue@3.5.35(typescript@6.0.3)) + vue: 3.5.35(typescript@6.0.3) + vueuc: 0.4.65(vue@3.5.35(typescript@6.0.3)) + + nanoid@3.3.12: {} + + oxfmt@0.54.0: + dependencies: + tinypool: 2.1.0 + optionalDependencies: + '@oxfmt/binding-android-arm-eabi': 0.54.0 + '@oxfmt/binding-android-arm64': 0.54.0 + '@oxfmt/binding-darwin-arm64': 0.54.0 + '@oxfmt/binding-darwin-x64': 0.54.0 + '@oxfmt/binding-freebsd-x64': 0.54.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.54.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.54.0 + '@oxfmt/binding-linux-arm64-gnu': 0.54.0 + '@oxfmt/binding-linux-arm64-musl': 0.54.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.54.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.54.0 + '@oxfmt/binding-linux-riscv64-musl': 0.54.0 + '@oxfmt/binding-linux-s390x-gnu': 0.54.0 + '@oxfmt/binding-linux-x64-gnu': 0.54.0 + '@oxfmt/binding-linux-x64-musl': 0.54.0 + '@oxfmt/binding-openharmony-arm64': 0.54.0 + '@oxfmt/binding-win32-arm64-msvc': 0.54.0 + '@oxfmt/binding-win32-ia32-msvc': 0.54.0 + '@oxfmt/binding-win32-x64-msvc': 0.54.0 + + path-browserify@1.0.1: {} + + picocolors@1.1.1: {} + + picomatch@4.0.4: {} + + postcss@8.5.15: + dependencies: + nanoid: 3.3.12 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + rate-limiter-flexible@5.0.5: {} + + rolldown@1.0.3: + dependencies: + '@oxc-project/types': 0.133.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.0.3 + '@rolldown/binding-darwin-arm64': 1.0.3 + '@rolldown/binding-darwin-x64': 1.0.3 + '@rolldown/binding-freebsd-x64': 1.0.3 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.3 + '@rolldown/binding-linux-arm64-gnu': 1.0.3 + '@rolldown/binding-linux-arm64-musl': 1.0.3 + '@rolldown/binding-linux-ppc64-gnu': 1.0.3 + '@rolldown/binding-linux-s390x-gnu': 1.0.3 + '@rolldown/binding-linux-x64-gnu': 1.0.3 + '@rolldown/binding-linux-x64-musl': 1.0.3 + '@rolldown/binding-openharmony-arm64': 1.0.3 + '@rolldown/binding-wasm32-wasi': 1.0.3 + '@rolldown/binding-win32-arm64-msvc': 1.0.3 + '@rolldown/binding-win32-x64-msvc': 1.0.3 + + seemly@0.3.10: {} + + source-map-js@1.2.1: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + tinypool@2.1.0: {} + + treemate@0.3.11: {} + + tslib@2.8.1: + optional: true + + typescript@6.0.3: {} + + undici-types@7.18.2: {} + + vdirs@0.1.8(vue@3.5.35(typescript@6.0.3)): + dependencies: + evtd: 0.2.4 + vue: 3.5.35(typescript@6.0.3) + + vite@8.0.16(@types/node@24.13.1): + dependencies: + lightningcss: 1.32.0 + picomatch: 4.0.4 + postcss: 8.5.15 + rolldown: 1.0.3 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 24.13.1 + fsevents: 2.3.3 + + vooks@0.2.12(vue@3.5.35(typescript@6.0.3)): + dependencies: + evtd: 0.2.4 + vue: 3.5.35(typescript@6.0.3) + + vscode-uri@3.1.0: {} + + vue-tsc@3.3.4(typescript@6.0.3): + dependencies: + '@volar/typescript': 2.4.28 + '@vue/language-core': 3.3.4 + typescript: 6.0.3 + + vue@3.5.35(typescript@6.0.3): + dependencies: + '@vue/compiler-dom': 3.5.35 + '@vue/compiler-sfc': 3.5.35 + '@vue/runtime-dom': 3.5.35 + '@vue/server-renderer': 3.5.35(vue@3.5.35(typescript@6.0.3)) + '@vue/shared': 3.5.35 + optionalDependencies: + typescript: 6.0.3 + + vueuc@0.4.65(vue@3.5.35(typescript@6.0.3)): + dependencies: + '@css-render/vue3-ssr': 0.15.14(vue@3.5.35(typescript@6.0.3)) + '@juggle/resize-observer': 3.4.0 + css-render: 0.15.14 + evtd: 0.2.4 + seemly: 0.3.10 + vdirs: 0.1.8(vue@3.5.35(typescript@6.0.3)) + vooks: 0.2.12(vue@3.5.35(typescript@6.0.3)) + vue: 3.5.35(typescript@6.0.3) diff --git a/code/ai/ai-vue/public/favicon.svg b/code/ai/ai-vue/public/favicon.svg new file mode 100644 index 00000000..6893eb13 --- /dev/null +++ b/code/ai/ai-vue/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/code/ai/ai-vue/public/icons.svg b/code/ai/ai-vue/public/icons.svg new file mode 100644 index 00000000..e9522193 --- /dev/null +++ b/code/ai/ai-vue/public/icons.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/ai/ai-vue/src/App.vue b/code/ai/ai-vue/src/App.vue new file mode 100644 index 00000000..637fea95 --- /dev/null +++ b/code/ai/ai-vue/src/App.vue @@ -0,0 +1,425 @@ + + + + + diff --git a/code/ai/ai-vue/src/api/file.ts b/code/ai/ai-vue/src/api/file.ts new file mode 100644 index 00000000..978c8202 --- /dev/null +++ b/code/ai/ai-vue/src/api/file.ts @@ -0,0 +1,62 @@ +import { createAlova } from "alova"; +import adapterFetch from "alova/fetch"; + +export type UploadedFile = { + originalName: string; + fileName: string; + url: string; + size: number; + contentType: string | null; +}; + +export type RecognitionRequest = { + url: string; + type: string; +}; + +export type RecognitionResponse = { + url: string; + type: string; + ppStructureResult: unknown; +}; + +const defaultApiBaseUrl = "http://192.168.1.137:8082"; + +function normalizeBaseUrl(baseUrl: string | undefined) { + const value = baseUrl?.trim() || defaultApiBaseUrl; + return value.endsWith("/") ? value.slice(0, -1) : value; +} + +async function readErrorMessage(response: Response) { + const fallback = `请求失败,状态码 ${response.status}`; + + try { + const data = await response.json(); + return data.detail || data.message || data.error || fallback; + } catch { + return fallback; + } +} + +const http = createAlova({ + baseURL: normalizeBaseUrl(import.meta.env.VITE_API_BASE_URL), + requestAdapter: adapterFetch(), + responded: async (response) => { + if (!response.ok) { + throw new Error(await readErrorMessage(response)); + } + + return response.json(); + }, +}); + +export function uploadFile(file: File) { + const formData = new FormData(); + formData.append("file", file); + + return http.Post("/api/files/upload", formData).send(); +} + +export function recognizeDocument(data: RecognitionRequest) { + return http.Post("/api/recognitions/start", data).send(); +} diff --git a/code/ai/ai-vue/src/assets/hero.png b/code/ai/ai-vue/src/assets/hero.png new file mode 100644 index 0000000000000000000000000000000000000000..02251f4b956c55af2d76fd0788124d7eee2b45eb GIT binary patch literal 13057 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 P00000NkvXXu0mjft=yBf literal 0 HcmV?d00001 diff --git a/code/ai/ai-vue/src/assets/vite.svg b/code/ai/ai-vue/src/assets/vite.svg new file mode 100644 index 00000000..5101b674 --- /dev/null +++ b/code/ai/ai-vue/src/assets/vite.svg @@ -0,0 +1 @@ +Vite diff --git a/code/ai/ai-vue/src/assets/vue.svg b/code/ai/ai-vue/src/assets/vue.svg new file mode 100644 index 00000000..770e9d33 --- /dev/null +++ b/code/ai/ai-vue/src/assets/vue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/code/ai/ai-vue/src/components/HelloWorld.vue b/code/ai/ai-vue/src/components/HelloWorld.vue new file mode 100644 index 00000000..2fe0d76e --- /dev/null +++ b/code/ai/ai-vue/src/components/HelloWorld.vue @@ -0,0 +1,93 @@ + + + diff --git a/code/ai/ai-vue/src/main.ts b/code/ai/ai-vue/src/main.ts new file mode 100644 index 00000000..f3b84632 --- /dev/null +++ b/code/ai/ai-vue/src/main.ts @@ -0,0 +1,6 @@ +import { createApp } from "vue"; +import "./style.css"; +import App from "./App.vue"; +import naive from "naive-ui"; + +createApp(App).use(naive).mount("#app"); diff --git a/code/ai/ai-vue/src/style.css b/code/ai/ai-vue/src/style.css new file mode 100644 index 00000000..7c1096be --- /dev/null +++ b/code/ai/ai-vue/src/style.css @@ -0,0 +1,27 @@ +* { + box-sizing: border-box; +} + +html, +body, +#app { + width: 100%; + min-width: 320px; + height: 100%; + margin: 0; + overflow: hidden; +} + +body { + font-family: + Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", + sans-serif; + background: #f4f6f8; +} + +button, +input, +textarea, +select { + font: inherit; +} diff --git a/code/ai/ai-vue/tsconfig.app.json b/code/ai/ai-vue/tsconfig.app.json new file mode 100644 index 00000000..5c750c5d --- /dev/null +++ b/code/ai/ai-vue/tsconfig.app.json @@ -0,0 +1,14 @@ +{ + "extends": "@vue/tsconfig/tsconfig.dom.json", + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "types": ["vite/client"], + + /* Linting */ + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"] +} diff --git a/code/ai/ai-vue/tsconfig.json b/code/ai/ai-vue/tsconfig.json new file mode 100644 index 00000000..d32ff682 --- /dev/null +++ b/code/ai/ai-vue/tsconfig.json @@ -0,0 +1,4 @@ +{ + "files": [], + "references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }] +} diff --git a/code/ai/ai-vue/tsconfig.node.json b/code/ai/ai-vue/tsconfig.node.json new file mode 100644 index 00000000..d3c52ea6 --- /dev/null +++ b/code/ai/ai-vue/tsconfig.node.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "es2023", + "lib": ["ES2023"], + "module": "esnext", + "types": ["node"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["vite.config.ts"] +} diff --git a/code/ai/ai-vue/vite.config.ts b/code/ai/ai-vue/vite.config.ts new file mode 100644 index 00000000..b66b21bc --- /dev/null +++ b/code/ai/ai-vue/vite.config.ts @@ -0,0 +1,24 @@ +import { defineConfig, loadEnv } from "vite"; +import vue from "@vitejs/plugin-vue"; + +// https://vite.dev/config/ +export default defineConfig(({ mode }) => { + const env = loadEnv(mode, process.cwd(), ""); + const apiBaseUrl = env.VITE_API_BASE_URL || "http://192.168.1.137:8082"; + + return { + plugins: [vue()], + server: { + proxy: { + "/api": { + target: apiBaseUrl, + changeOrigin: true, + }, + "/uploads": { + target: apiBaseUrl, + changeOrigin: true, + }, + }, + }, + }; +}); diff --git a/code/ai/uploads/9a6506af761a427490b5392dc18c8cc5.jpg b/code/ai/uploads/9a6506af761a427490b5392dc18c8cc5.jpg new file mode 100644 index 0000000000000000000000000000000000000000..39283c9ca389073760126191e237b50db1be781c GIT binary patch literal 38809 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 literal 0 HcmV?d00001 diff --git a/code/ai/uploads/ba1e6df6b8e04f0baaad991b33602b1b.pdf b/code/ai/uploads/ba1e6df6b8e04f0baaad991b33602b1b.pdf new file mode 100644 index 0000000000000000000000000000000000000000..831cb7a19fb928508ad16e1ca17ae288e2b2e2cb GIT binary patch literal 37544 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 + diff --git a/code/docusaurus/docusaurus.config.ts b/code/docusaurus/docusaurus.config.ts index 7f4aa0af..4b1d1ebe 100644 --- a/code/docusaurus/docusaurus.config.ts +++ b/code/docusaurus/docusaurus.config.ts @@ -108,11 +108,6 @@ const config: Config = { position: 'left', sidebarId: 'DatabaseSidebar', }, - { - to: '/docs/resources', - label: '资源导航', - position: 'left', - }, { to: '/blog', label: '博客', diff --git a/code/docusaurus/src/components/Link/LinkCard.tsx b/code/docusaurus/src/components/Link/LinkCard.tsx index 4798c444..d57b4812 100644 --- a/code/docusaurus/src/components/Link/LinkCard.tsx +++ b/code/docusaurus/src/components/Link/LinkCard.tsx @@ -1,11 +1,19 @@ import styles from './styles.module.css' +export type LinkCardTagType = 'default' | 'success' | 'warning' | 'error' | 'info' + +export interface LinkCardTag { + label: string + type?: LinkCardTagType +} + export interface LinkCardProps { title: string description: string href: string icon?: string note?: string + tags?: LinkCardTag[] } function getHostname(href: string) { @@ -23,6 +31,7 @@ export default function LinkCard({ href, icon, note, + tags, }: LinkCardProps) { const hostname = getHostname(href) const favicon = icon || (hostname ? `https://www.google.com/s2/favicons?domain=${hostname}&sz=64` : undefined) @@ -34,17 +43,29 @@ export default function LinkCard({ target="_blank" rel="noopener noreferrer" > - + {favicon ? : {title.charAt(0)}} - {title} + {title} {description} {hostname && {hostname}} + {tags && tags.length > 0 && ( + + {tags.map((tag, index) => ( + + {tag.label} + + ))} + + )} {note && ( - - 备注 + + 备注: {note} )} diff --git a/code/docusaurus/src/components/Link/LinkGrid.tsx b/code/docusaurus/src/components/Link/LinkGrid.tsx index 561b40e6..53f782af 100644 --- a/code/docusaurus/src/components/Link/LinkGrid.tsx +++ b/code/docusaurus/src/components/Link/LinkGrid.tsx @@ -1,5 +1,4 @@ import React from 'react' -import styles from './styles.module.css' interface Props { children: React.ReactNode @@ -7,7 +6,7 @@ interface Props { export default function LinkGrid({ children }: Props) { return ( -
+
{children}
) diff --git a/code/docusaurus/src/components/Link/SearchableLinkGrid.tsx b/code/docusaurus/src/components/Link/SearchableLinkGrid.tsx index 43163ae2..618d64ce 100644 --- a/code/docusaurus/src/components/Link/SearchableLinkGrid.tsx +++ b/code/docusaurus/src/components/Link/SearchableLinkGrid.tsx @@ -101,12 +101,6 @@ export default function SearchableLinkGrid({ groups }: SearchableLinkGridProps)