feat: sync full workspace including web modules, docs, and configurations to Gitea

Optimized the root .gitignore to exclude virtual environments, node modules,
and temp folders to ensure clean and lightweight version tracking.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
王冕
2026-06-09 18:12:25 +08:00
parent 351688006e
commit a27e3b8e43
1510 changed files with 162044 additions and 1517 deletions

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
:root{color-scheme:light;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}html,body,#canvas-root,.canvas-template-shell,.canvas-template-canvas{width:100%;height:100%;min-height:100%;min-width:0}body{margin:0;overflow:hidden;background:#f1f5f9;color:#475569}body.dark{background:#0f172a;color:#94a3b8}.canvas-template-shell{background:inherit}.canvas-template-placeholder,.canvas-template-status,.canvas-template-error{display:flex;align-items:center;justify-content:center;width:100%;height:100%;padding:24px;box-sizing:border-box;font-size:12px;line-height:1.5}.canvas-template-status,.canvas-template-placeholder{color:inherit}.canvas-template-error{color:#dc2626}body.dark .canvas-template-error{color:#fca5a5}

View File

@@ -0,0 +1 @@
import{r as n,j as c,d as R}from"./chunks/vendor-react.js?v=1775123024591";import{I as T}from"./chunks/vendor-excalidraw.js?v=1775123024591";import"./chunks/_commonjsHelpers.js?v=1775123024591";import"./chunks/preload-helper.js?v=1775123024591";import"./chunks/vendor-common.js?v=1775123024591";import"./chunks/_commonjs-dynamic-modules.js?v=1775123024591";const N=1500;function S({canvasName:t,isDarkMode:e}){const[s,i]=n.useState(null),[l,v]=n.useState(null),[I,h]=n.useState(!0),[g,w]=n.useState(""),u=n.useRef(null),d=n.useRef(!1),C=n.useRef(t),f=n.useRef(!1);n.useEffect(()=>{C.current=t,f.current=!1,h(!0),w(""),v(null);let r=!1;return(async()=>{try{const a=await fetch(`/api/canvas/${encodeURIComponent(t)}`);if(r)return;if(!a.ok)throw new Error(`加载画布失败 (${a.status})`);const m=await a.json();if(r)return;v(m),f.current=!0}catch(a){if(r)return;w((a==null?void 0:a.message)||"加载画布失败")}finally{r||h(!1)}})(),()=>{r=!0}},[t]);const E=n.useCallback(async(r,o)=>{if(!d.current){d.current=!0;try{const a=(s==null?void 0:s.getFiles())||{},m={type:"excalidraw",version:2,source:"axhub-make",elements:r,appState:{gridSize:(o==null?void 0:o.gridSize)??null,viewBackgroundColor:(o==null?void 0:o.viewBackgroundColor)??"#ffffff"},files:a};await fetch(`/api/canvas/${encodeURIComponent(C.current)}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({content:JSON.stringify(m,null,2)})})}catch(a){console.warn("Failed to save canvas:",a)}finally{d.current=!1}}},[s]),j=n.useCallback((r,o)=>{f.current&&(u.current&&clearTimeout(u.current),u.current=setTimeout(()=>{E(r,o)},N))},[E]);return n.useEffect(()=>()=>{u.current&&clearTimeout(u.current)},[]),I?c.jsx("div",{className:"canvas-template-status",children:"加载中..."}):g?c.jsx("div",{className:"canvas-template-error",children:g}):c.jsx("div",{className:"canvas-template-canvas",children:c.jsx(T,{excalidrawAPI:r=>i(r),initialData:l,onChange:j,theme:e?"dark":"light",UIOptions:{canvasActions:{saveAsImage:!0,export:!1}}},t)})}const p="axhub-make-dark-mode";function x(t){var e;return typeof document>"u"?"":((e=document.querySelector(`meta[name="${t}"]`))==null?void 0:e.getAttribute("content"))||""}function y(t){const e=String(t||"").trim();return!e||e.startsWith("{{")?"":e}function b(t){const e=t.match(/^\/canvas\/(.+?)\/?$/);if(!(e!=null&&e[1]))return"";try{return decodeURIComponent(e[1])}catch{return e[1]}}function A(){const t=y(x("axhub-canvas-name"));return t||(typeof window>"u"?"":b(window.location.pathname))}function D(t){const e=y(x("axhub-canvas-title"));if(e)return e;const s=t.replace(/\.excalidraw$/i,"").trim();return s?`${s} - Canvas`:"Canvas"}function M(){try{return localStorage.getItem(p)==="true"}catch{return!1}}function $(){const[t]=n.useState(()=>A()),[e,s]=n.useState(()=>M());return n.useEffect(()=>{const i=l=>{l.key===p&&s(l.newValue==="true")};return window.addEventListener("storage",i),()=>{window.removeEventListener("storage",i)}},[]),n.useEffect(()=>{const i=D(t);document.title=i,document.documentElement.classList.toggle("dark",e),document.body.classList.toggle("dark",e)},[t,e]),c.jsx("div",{className:e?"canvas-template-shell dark":"canvas-template-shell",children:t?c.jsx(S,{canvasName:t,isDarkMode:e}):c.jsx("div",{className:"canvas-template-placeholder",children:"未指定画布"})})}const k=document.getElementById("canvas-root");if(!k)throw new Error("[Canvas Template] 找不到 #canvas-root 元素");const L=R.createRoot(k);L.render(c.jsx($,{}));

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
function r(o){throw new Error('Could not dynamically require "'+o+'". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.')}export{r as c};

View File

@@ -0,0 +1 @@
var u=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function f(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function l(e){if(e.__esModule)return e;var r=e.default;if(typeof r=="function"){var t=function o(){return this instanceof o?Reflect.construct(r,arguments,this.constructor):r.apply(this,arguments)};t.prototype=r.prototype}else t={};return Object.defineProperty(t,"__esModule",{value:!0}),Object.keys(e).forEach(function(o){var n=Object.getOwnPropertyDescriptor(e,o);Object.defineProperty(t,o,n.get?n:{enumerable:!0,get:function(){return e[o]}})}),t}export{f as a,u as c,l as g};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
const h="modulepreload",E=function(i){return"/"+i},a={},y=function(u,s,v){let c=Promise.resolve();if(s&&s.length>0){document.getElementsByTagName("link");const e=document.querySelector("meta[property=csp-nonce]"),t=(e==null?void 0:e.nonce)||(e==null?void 0:e.getAttribute("nonce"));c=Promise.allSettled(s.map(r=>{if(r=E(r),r in a)return;a[r]=!0;const o=r.endsWith(".css"),d=o?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${r}"]${d}`))return;const n=document.createElement("link");if(n.rel=o?"stylesheet":h,o||(n.as="script"),n.crossOrigin="",n.href=r,t&&n.setAttribute("nonce",t),document.head.appendChild(n),o)return new Promise((f,m)=>{n.addEventListener("load",f),n.addEventListener("error",()=>m(new Error(`Unable to preload CSS for ${r}`)))})}))}function l(e){const t=new Event("vite:preloadError",{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return c.then(e=>{for(const t of e||[])t.status==="rejected"&&l(t.reason);return u().catch(l)})};export{y as _};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 745 B

View File

@@ -0,0 +1 @@
import{e as i,f as c,R as s,c as l}from"./chunks/vendor-react.js?v=1775123024591";import{A as m}from"./chunks/vendor-antd.js?v=1775123024591";import"./chunks/_commonjsHelpers.js?v=1775123024591";import"./chunks/vendor-common.js?v=1775123024591";import"./chunks/preload-helper.js?v=1775123024591";import"./chunks/_commonjs-dynamic-modules.js?v=1775123024591";function f(r,n){const e=document.getElementById("root");if(!e){console.error("[Html Template] 找不到 #root 元素");return}const o=n||{container:e,config:{},data:{},events:{}};try{l(e).render(s.createElement(m,null,s.createElement(r,o)))}catch(t){console.error("[Html Template] 渲染失败:",t)}}const p={...c,...i};if(typeof window<"u"){const r=new URLSearchParams(window.location.search),n=r.get("scale"),e=r.get("width"),a=r.get("height"),o=document.getElementById("root");if(o){if(n){const t=parseFloat(n);!isNaN(t)&&t>0&&(o.style.transform=`scale(${t})`,o.style.transformOrigin="top left")}if(e||a){if(e){const t=parseInt(e);!isNaN(t)&&t>0&&(o.style.width=`${t}px`)}if(a){const t=parseInt(a);!isNaN(t)&&t>0&&(o.style.height=`${t}px`)}}}window.HtmlTemplateBootstrap={renderComponent:f,React:s,ReactDOM:p}}

View File

@@ -0,0 +1 @@
.resize-handle-right{cursor:ew-resize!important;z-index:100!important}.resize-handle-right:before{content:"";position:absolute;right:50%;top:50%;transform:translate(50%,-50%);width:2px;height:40px;background:hsl(var(--border));border-radius:1px;transition:all .2s;z-index:100}.resize-handle-right:hover:before,.resize-handle-right:active:before{background:hsl(var(--muted-foreground));width:3px;height:50px}.resize-handle-bottom{cursor:ns-resize!important;z-index:100!important}.resize-handle-bottom:before{content:"";position:absolute;left:50%;bottom:50%;transform:translate(-50%,50%);width:40px;height:2px;background:hsl(var(--border));border-radius:1px;transition:all .2s;z-index:100}.resize-handle-bottom:hover:before,.resize-handle-bottom:active:before{background:hsl(var(--muted-foreground));width:50px;height:3px}.resize-handle-bottomRight{cursor:nwse-resize!important;z-index:100!important}.resize-handle-bottomRight:before{content:"";position:absolute;right:0;bottom:0;width:12px;height:12px;background:hsl(var(--border));border-radius:0 0 6px;transition:all .2s;z-index:100}.resize-handle-bottomRight:hover:before,.resize-handle-bottomRight:active:before{background:hsl(var(--border-strong));width:16px;height:16px}.ant-card-hoverable:hover .resize-handle-right:before,.ant-card-hoverable:hover .resize-handle-bottom:before,.ant-card-hoverable:hover .resize-handle-bottomRight:before{background:hsl(var(--border-strong))}.menu-item-wrapper .more-btn{opacity:0;transition:opacity .2s,color .2s}.menu-item-wrapper:hover .more-btn,.menu-item-wrapper.is-selected .more-btn,.menu-item-wrapper .more-btn.ant-dropdown-open,.menu-item-wrapper .more-btn[data-state=open],.menu-item-wrapper .more-btn:focus-visible{opacity:1}.menu-item-wrapper .prototype-row-action{opacity:0;transition:opacity .2s,color .2s}.menu-item-wrapper.is-selected .prototype-row-action{opacity:0}.menu-item-wrapper:hover .prototype-row-action,.menu-item-wrapper .prototype-row-action.ant-dropdown-open,.menu-item-wrapper .prototype-row-action[data-state=open],.menu-item-wrapper .prototype-row-action:focus-visible{opacity:1}@media (max-width: 768px){.pc-layout{display:none!important}.mobile-layout{display:block!important}}@media (min-width: 769px){.pc-layout{display:block!important}.mobile-layout{display:none!important}}.mobile-item-card{background:var(--mobile-item-bg, hsl(var(--card)));border:1px solid var(--mobile-item-border, hsl(var(--border)));border-radius:8px;padding:16px;margin-bottom:12px;cursor:pointer;transition:all .2s}.mobile-item-card:hover{border-color:var(--mobile-item-hover-border, hsl(var(--ring)));box-shadow:var(--mobile-item-hover-shadow, var(--shadow-sm))}.mobile-item-card:active{transform:scale(.98)}.mobile-item-title{font-size:16px;font-weight:500;color:var(--mobile-item-title-color, hsl(var(--foreground)));margin-bottom:4px}.mobile-item-name{font-size:12px;color:var(--mobile-item-name-color, hsl(var(--muted-foreground)))}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,246 @@
/**
* 客户端自动调试工具
* 在页面加载时自动检测错误并上报到服务器
*
* 功能:
* 1. 白屏检测
* 2. 运行时错误捕获
* 3. 自动上报到服务器
* 4. 提供调试建议
*/
(function() {
'use strict';
const AUTO_DEBUG_CONFIG = {
enabled: true,
whiteScreenTimeout: 3000, // 3秒后检测白屏
reportUrl: '/api/report-error',
autoReport: true
};
// 获取当前页面路径
function getCurrentPath() {
const pathname = window.location.pathname;
// 从 /prototypes/xxx.html 或 /components/xxx.html 提取路径
const match = pathname.match(/\/(prototypes|components)\/([^.]+)/);
return match ? `${match[1]}/${match[2]}` : null;
}
// 上报错误到服务器
function reportError(error) {
if (!AUTO_DEBUG_CONFIG.autoReport) return;
const currentPath = getCurrentPath();
if (!currentPath) return;
fetch(AUTO_DEBUG_CONFIG.reportUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
path: currentPath,
error: {
message: error.message,
stack: error.stack,
type: error.type || 'error',
timestamp: Date.now()
}
})
})
.then(res => res.json())
.then(data => {
if (data.suggestion) {
console.log('%c[Auto Debug] 修复建议:', 'color: #1890ff; font-weight: bold;');
console.log('%c' + data.suggestion, 'color: #52c41a;');
}
})
.catch(err => {
console.error('[Auto Debug] 上报错误失败:', err);
});
}
// 白屏检测
function checkWhiteScreen() {
const root = document.getElementById('root');
const hasContent = root && root.children.length > 0;
const hasError = document.getElementById('__fallback_error_overlay__');
if (!hasContent && !hasError) {
console.error('%c[Auto Debug] 检测到白屏问题', 'color: #ff4d4f; font-weight: bold;');
// 诊断白屏原因
const diagnostics = {
rootExists: !!root,
reactExists: !!window.React,
reactDOMExists: !!window.ReactDOM,
componentExists: !!window.AxhubDevComponent,
bootstrapExists: !!window.DevTemplateBootstrap,
errorQueueLength: window.__ERROR_SYSTEM__?.getErrorQueue()?.length || 0
};
console.log('%c[Auto Debug] 诊断信息:', 'color: #1890ff;', diagnostics);
// 提供修复建议
const suggestions = [];
if (!diagnostics.rootExists) {
suggestions.push('❌ #root 元素不存在,检查 HTML 模板');
}
if (!diagnostics.reactExists) {
suggestions.push('❌ React 未加载,检查 bootstrap 脚本');
}
if (!diagnostics.componentExists) {
suggestions.push('❌ 组件未加载,检查组件导出和导入路径');
}
if (diagnostics.errorQueueLength > 0) {
suggestions.push(`⚠️ 发现 ${diagnostics.errorQueueLength} 个错误,可能导致渲染失败`);
}
if (suggestions.length === 0) {
suggestions.push('⚠️ 未发现明显问题,可能是组件渲染逻辑错误');
}
console.log('%c[Auto Debug] 修复建议:', 'color: #faad14; font-weight: bold;');
suggestions.forEach(s => console.log('%c ' + s, 'color: #faad14;'));
// 上报白屏问题
reportError({
message: 'White screen detected',
stack: JSON.stringify(diagnostics),
type: 'white-screen'
});
} else {
console.log('%c[Auto Debug] 页面渲染正常 ✓', 'color: #52c41a; font-weight: bold;');
}
}
// 性能监控
function monitorPerformance() {
if (!window.performance || !window.performance.timing) return;
window.addEventListener('load', function() {
setTimeout(function() {
const timing = window.performance.timing;
const loadTime = timing.loadEventEnd - timing.navigationStart;
const domReady = timing.domContentLoadedEventEnd - timing.navigationStart;
const renderTime = timing.domComplete - timing.domLoading;
console.log('%c[Auto Debug] 性能指标:', 'color: #1890ff; font-weight: bold;');
console.log(` 页面加载时间: ${loadTime}ms`);
console.log(` DOM 就绪时间: ${domReady}ms`);
console.log(` 渲染时间: ${renderTime}ms`);
if (loadTime > 5000) {
console.warn('%c[Auto Debug] 页面加载较慢,可能影响用户体验', 'color: #faad14;');
}
}, 0);
});
}
// 组件渲染监控
function monitorComponentRender() {
// 监听 React 组件挂载
const originalCreateElement = window.React?.createElement;
if (originalCreateElement) {
let renderCount = 0;
window.React.createElement = function() {
renderCount++;
return originalCreateElement.apply(this, arguments);
};
setTimeout(function() {
console.log(`%c[Auto Debug] React 渲染次数: ${renderCount}`, 'color: #1890ff;');
if (renderCount === 0) {
console.warn('%c[Auto Debug] React 未执行任何渲染,可能是组件问题', 'color: #faad14;');
}
}, 2000);
}
}
// 依赖检查
function checkDependencies() {
const requiredGlobals = {
'React': window.React,
'ReactDOM': window.ReactDOM,
'DevTemplateBootstrap': window.DevTemplateBootstrap
};
const missing = [];
Object.keys(requiredGlobals).forEach(function(name) {
if (!requiredGlobals[name]) {
missing.push(name);
}
});
if (missing.length > 0) {
console.error('%c[Auto Debug] 缺少必需的全局变量:', 'color: #ff4d4f; font-weight: bold;', missing);
return false;
}
console.log('%c[Auto Debug] 依赖检查通过 ✓', 'color: #52c41a;');
return true;
}
// 初始化
function init() {
if (!AUTO_DEBUG_CONFIG.enabled) return;
console.log('%c[Auto Debug] 自动调试工具已启用', 'color: #1890ff; font-weight: bold;');
// 检查依赖
checkDependencies();
// 监控性能
monitorPerformance();
// 监控组件渲染
monitorComponentRender();
// 延迟检测白屏
setTimeout(checkWhiteScreen, AUTO_DEBUG_CONFIG.whiteScreenTimeout);
// 集成错误捕获系统
if (window.__ERROR_SYSTEM__) {
const originalAddError = window.__ERROR_SYSTEM__.addError;
window.__ERROR_SYSTEM__.addError = function(message, stack) {
// 调用原始方法
if (originalAddError) {
originalAddError.call(this, message, stack);
}
// 上报错误
reportError({
message: message,
stack: stack,
type: 'manual'
});
};
}
}
// 暴露 API
window.__AUTO_DEBUG__ = {
config: AUTO_DEBUG_CONFIG,
checkWhiteScreen: checkWhiteScreen,
reportError: reportError,
checkDependencies: checkDependencies,
// 手动触发检测
runDiagnostics: function() {
console.log('%c[Auto Debug] 开始诊断...', 'color: #1890ff; font-weight: bold;');
checkDependencies();
checkWhiteScreen();
}
};
// 页面加载完成后初始化
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
console.log('%c[Auto Debug] 客户端调试工具已加载', 'color: #52c41a; font-weight: bold;');
})();

View File

@@ -0,0 +1,57 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="axhub-canvas-name" content="{{CANVAS_NAME}}">
<meta name="axhub-canvas-title" content="{{CANVAS_TITLE}}">
<title>Canvas</title>
<link rel="stylesheet" href="/assets/vendor-excalidraw.css?v=1775123024591">
<link rel="stylesheet" href="/assets/canvas-template-bootstrap.css?v=1775123024591">
<style>
html,
body,
#canvas-root {
width: 100%;
height: 100%;
min-height: 100%;
margin: 0;
}
body {
overflow: hidden;
background: hsl(220 14% 96%);
}
.canvas-template-shell {
width: 100%;
height: 100%;
}
.canvas-template-placeholder {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
color: hsl(215 16% 47%);
font-size: 12px;
}
</style>
</head>
<body>
<div id="canvas-root">
<div class="canvas-template-placeholder">加载画布编辑器...</div>
</div>
<script type="module">
import '/@vite/client';
import '@vitejs/plugin-react/preamble';
</script>
<script type="module" src="/assets/canvas-template-bootstrap.js?v=1775123024591"></script>
</body>
</html>

View File

@@ -0,0 +1,569 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>{{TITLE}}</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<style>
html,
body {
box-sizing: border-box;
width: 100%;
margin: 0;
padding: 0;
height: 100%;
min-height: 100%;
overflow-x: hidden;
overflow-y: auto;
}
#root {
width: 100%;
margin-left: auto;
margin-right: auto;
height: 100%;
min-height: 100vh;
overflow: visible;
}
/* 如果是元素演示页面(可能被内嵌到 iframe设置固定尺寸 */
body.is-element-page #root {
width: 100vw;
height: 100vh;
}
/* 平板和手机模式隐藏滚动条 */
@media (max-width: 1024px) {
::-webkit-scrollbar {
display: none;
}
html,
body {
scrollbar-width: none;
/* Firefox */
}
}
</style>
</head>
<body>
<script>
// 全局错误捕获系统(增强版)
(function () {
// 等待 DevTemplateBootstrap 加载后检查 inspecta 模式
// 如果是 inspecta 模式则禁用错误捕获
function checkInspectaMode() {
if (
window.DevTemplateBootstrap &&
(window.DevTemplateBootstrap.inspectaMode ||
window.DevTemplateBootstrap.editors?.getMode?.() === 'inspecta')
) {
console.log('%c[Error System] Inspecta 模式已启用,错误捕获已禁用', 'color: #faad14; font-weight: bold;');
return true;
}
return false;
}
// 先快速检查 URL 参数(避免在 bootstrap 加载前就触发错误)
const urlParams = new URLSearchParams(window.location.search);
const editorParam = urlParams.get('editor');
if (editorParam === 'inspecta' || urlParams.get('inspecta') === 'true') {
console.log('%c[Error System] 检测到 inspecta 参数,错误捕获已禁用', 'color: #faad14; font-weight: bold;');
return;
}
const bootTime = Date.now();
const errorQueue = [];
let reactReady = false;
let fallbackUIShown = false;
// 保存原始的 console 方法
const originalConsoleError = console.error;
const originalConsoleWarn = console.warn;
// 简易版错误显示(降级方案)
function showFallbackErrorUI(errors) {
if (fallbackUIShown) {
// 更新已有的错误列表
const errorList = document.getElementById('__fallback_error_list__');
if (errorList) {
errorList.innerHTML = errors.map((err, idx) =>
'<div style="margin-bottom: 12px; padding: 8px; background: #fff1f0; border-left: 3px solid #ff4d4f; border-radius: 2px;">' +
'<div style="font-weight: 600; color: #cf1322; margin-bottom: 4px;">[' + (idx + 1) + '] ' + escapeHtml(err.message) + '</div>' +
(err.stack ? '<pre style="margin: 0; font-size: 11px; color: #666; overflow-x: auto; white-space: pre-wrap; word-break: break-all;">' + escapeHtml(err.stack) + '</pre>' : '') +
'</div>'
).join('');
}
return;
}
fallbackUIShown = true;
const overlay = document.createElement('div');
overlay.id = '__fallback_error_overlay__';
overlay.style.cssText = 'position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.45); z-index: 999999; display: flex; align-items: flex-start; justify-content: center; padding: 40px 20px; overflow: auto;';
const modal = document.createElement('div');
modal.style.cssText = 'background: white; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); max-width: 700px; width: 100%; max-height: 80vh; overflow: hidden; display: flex; flex-direction: column;';
modal.innerHTML =
'<div style="padding: 16px 24px; border-bottom: 1px solid #f0f0f0; display: flex; align-items: center; justify-content: space-between;">' +
'<div style="display: flex; align-items: center; gap: 8px;">' +
'<svg viewBox="64 64 896 896" width="20" height="20" fill="#ff4d4f"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 0 1 0-96 48.01 48.01 0 0 1 0 96z"></path></svg>' +
'<span style="font-size: 16px; font-weight: 600; color: #262626;">运行时错误 (' + errors.length + ')</span>' +
'</div>' +
'<button id="__fallback_close__" style="border: none; background: none; cursor: pointer; font-size: 20px; color: #8c8c8c; padding: 0; width: 24px; height: 24px; display: flex; align-items: center; justify-content: center;">×</button>' +
'</div>' +
'<div id="__fallback_error_list__" style="padding: 24px; overflow-y: auto; flex: 1;">' +
errors.map((err, idx) =>
'<div style="margin-bottom: 12px; padding: 8px; background: #fff1f0; border-left: 3px solid #ff4d4f; border-radius: 2px;">' +
'<div style="font-weight: 600; color: #cf1322; margin-bottom: 4px;">[' + (idx + 1) + '] ' + escapeHtml(err.message) + '</div>' +
(err.stack ? '<pre style="margin: 0; font-size: 11px; color: #666; overflow-x: auto; white-space: pre-wrap; word-break: break-all;">' + escapeHtml(err.stack) + '</pre>' : '') +
'</div>'
).join('') +
'</div>' +
'<div style="padding: 12px 24px; border-top: 1px solid #f0f0f0; display: flex; gap: 8px; justify-content: flex-end;">' +
'<button id="__fallback_copy__" style="padding: 6px 16px; border: 1px solid #d9d9d9; background: white; border-radius: 4px; cursor: pointer; font-size: 14px;">复制错误</button>' +
'<button id="__fallback_clear__" style="padding: 6px 16px; border: 1px solid #d9d9d9; background: white; border-radius: 4px; cursor: pointer; font-size: 14px;">清空并关闭</button>' +
'</div>';
overlay.appendChild(modal);
document.body.appendChild(overlay);
// 绑定事件
document.getElementById('__fallback_close__').onclick = function() {
overlay.style.display = 'none';
};
document.getElementById('__fallback_clear__').onclick = function() {
errorQueue.length = 0;
document.body.removeChild(overlay);
fallbackUIShown = false;
};
document.getElementById('__fallback_copy__').onclick = function() {
const text = errors.map((err, idx) =>
'[' + (idx + 1) + '] ' + err.message + '\n' + (err.stack || '无堆栈信息')
).join('\n\n' + '='.repeat(80) + '\n\n');
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(function() {
showFallbackNotice('错误信息已复制到剪贴板');
}).catch(function() {
showFallbackTextPanel('请手动复制以下错误信息', text);
});
} else {
showFallbackTextPanel('请手动复制以下错误信息', text);
}
};
}
function showFallbackNotice(message) {
const existing = document.getElementById('__fallback_notice__');
if (existing) {
existing.remove();
}
const notice = document.createElement('div');
notice.id = '__fallback_notice__';
notice.textContent = message;
notice.style.cssText = 'position: fixed; right: 24px; bottom: 24px; z-index: 1000001; max-width: min(360px, calc(100vw - 48px)); padding: 10px 14px; border-radius: 8px; background: rgba(38, 38, 38, 0.92); color: white; font-size: 13px; line-height: 1.5; box-shadow: 0 8px 24px rgba(0,0,0,0.2);';
document.body.appendChild(notice);
window.setTimeout(function() {
notice.remove();
}, 2400);
}
function showFallbackTextPanel(title, text) {
const existing = document.getElementById('__fallback_text_panel__');
if (existing) {
existing.remove();
}
const overlay = document.createElement('div');
overlay.id = '__fallback_text_panel__';
overlay.style.cssText = 'position: fixed; inset: 0; background: rgba(0,0,0,0.45); z-index: 1000000; display: flex; align-items: center; justify-content: center; padding: 24px;';
const panel = document.createElement('div');
panel.style.cssText = 'width: min(720px, 100%); max-height: min(80vh, 640px); background: white; border-radius: 10px; box-shadow: 0 12px 32px rgba(0,0,0,0.2); display: flex; flex-direction: column; overflow: hidden;';
const header = document.createElement('div');
header.style.cssText = 'padding: 16px 20px; border-bottom: 1px solid #f0f0f0; font-size: 16px; font-weight: 600; color: #262626;';
header.textContent = title;
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.readOnly = true;
textarea.style.cssText = 'width: calc(100% - 40px); min-height: 280px; margin: 20px; padding: 12px; resize: vertical; border: 1px solid #d9d9d9; border-radius: 8px; font: 12px/1.6 SFMono-Regular, Consolas, monospace; color: #262626; background: #fafafa;';
const footer = document.createElement('div');
footer.style.cssText = 'padding: 12px 20px 20px; display: flex; justify-content: flex-end; gap: 8px;';
const selectButton = document.createElement('button');
selectButton.type = 'button';
selectButton.textContent = '全选内容';
selectButton.style.cssText = 'padding: 6px 16px; border: 1px solid #d9d9d9; background: white; border-radius: 6px; cursor: pointer; font-size: 14px;';
selectButton.onclick = function() {
textarea.focus();
textarea.select();
};
const closeButton = document.createElement('button');
closeButton.type = 'button';
closeButton.textContent = '关闭';
closeButton.style.cssText = 'padding: 6px 16px; border: 1px solid #1677ff; background: #1677ff; color: white; border-radius: 6px; cursor: pointer; font-size: 14px;';
closeButton.onclick = function() {
overlay.remove();
};
overlay.onclick = function(event) {
if (event.target === overlay) {
overlay.remove();
}
};
footer.appendChild(selectButton);
footer.appendChild(closeButton);
panel.appendChild(header);
panel.appendChild(textarea);
panel.appendChild(footer);
overlay.appendChild(panel);
document.body.appendChild(overlay);
window.setTimeout(function() {
textarea.focus();
textarea.select();
}, 0);
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
function isIgnorableVersionProxyIssue(text) {
const normalizedText = String(text || '');
return normalizedText.includes('__axhub_version__') &&
normalizedText.includes('html-proxy') &&
normalizedText.includes('No matching HTML proxy module found');
}
// 统一的错误处理函数
function handleError(message, stack, type) {
const error = {
message: message,
stack: stack,
timestamp: Date.now(),
sinceBoot: Date.now() - bootTime,
type: type
};
errorQueue.push(error);
// 如果 React 已就绪,使用 React 组件显示
if (reactReady && typeof window.showErrorDialog === 'function') {
window.showErrorDialog(message, stack);
} else {
// 否则使用降级 UI
showFallbackErrorUI(errorQueue);
}
}
// 捕获未处理的错误(捕获阶段,优先级最高)
window.addEventListener('error', function (event) {
originalConsoleError.call(console, '捕获到错误事件:', {
message: event.message,
error: event.error,
filename: event.filename,
lineno: event.lineno,
colno: event.colno,
target: event.target
});
try {
let message = event.message || '发生了一个未知错误';
let stack = '';
// 检查是否是资源加载错误
if (event.target !== window && event.target instanceof HTMLElement) {
const tagName = event.target.tagName;
const resourceUrl = event.target.src || event.target.href || '未知资源';
if (isIgnorableVersionProxyIssue(resourceUrl)) {
return;
}
message = `资源加载失败: ${tagName} - ${resourceUrl}`;
stack = `标签: <${tagName.toLowerCase()}>\nURL: ${resourceUrl}\n类型: 资源加载错误`;
originalConsoleError.call(console, '资源加载失败:', { tagName, resourceUrl });
} else if (event.error && event.error.stack) {
stack = event.error.stack;
} else if (event.error && event.error.message) {
message = event.error.message;
stack = '无详细堆栈信息';
} else if (event.filename) {
stack = event.filename + ':' + event.lineno + ':' + event.colno;
} else {
stack = '无堆栈信息';
}
handleError(message, stack, 'error');
} catch (err) {
originalConsoleError.call(console, '[Error Handler] 处理错误失败:', err);
}
event.preventDefault();
}, true); // ⚠️ 使用捕获阶段
// 捕获未处理的 Promise 拒绝
window.addEventListener('unhandledrejection', function (event) {
originalConsoleError.call(console, '捕获到未处理的 Promise 拒绝:', event.reason);
try {
const message = event.reason && event.reason.message
? event.reason.message
: String(event.reason || '未知 Promise 拒绝');
const stack = event.reason && event.reason.stack ? event.reason.stack : '';
handleError('Promise 拒绝: ' + message, stack, 'unhandledrejection');
} catch (err) {
originalConsoleError.call(console, '[Error Handler] 处理 Promise 拒绝失败:', err);
}
event.preventDefault();
});
// 全局开关:是否启用错误捕获
let errorCaptureEnabled = true;
// 拦截 console.error可选捕获库的错误输出
console.error = function () {
const args = Array.prototype.slice.call(arguments);
const message = args.join(' ');
// 先调用原始的 console.error
originalConsoleError.apply(console, args);
// 如果错误捕获被禁用,直接返回
if (!errorCaptureEnabled) {
return;
}
// 检查是否是重要错误
const lowerMessage = message.toLowerCase();
if (isIgnorableVersionProxyIssue(message)) {
return;
}
const shouldShow = !message.includes('[Error Dialog]') &&
!message.includes('[Error Handler]') &&
(lowerMessage.includes('error') || lowerMessage.includes('failed'));
if (shouldShow) {
try {
const stack = new Error().stack || '无堆栈信息';
handleError('控制台错误: ' + message, stack, 'console.error');
} catch (err) {
originalConsoleError.call(console, '[Error Handler] 显示对话框失败:', err);
}
}
};
// 暴露 React 就绪标记和控制接口
window.__ERROR_SYSTEM__ = {
markReactReady: function() {
reactReady = true;
console.log('%c[Error System] React 错误系统已就绪', 'color: #52c41a; font-weight: bold;');
// 如果有降级 UI 显示,迁移到 React 组件
if (fallbackUIShown && errorQueue.length > 0 && typeof window.showErrorDialog === 'function') {
const overlay = document.getElementById('__fallback_error_overlay__');
if (overlay) {
overlay.style.display = 'none';
}
errorQueue.forEach(function(err) {
window.showErrorDialog(err.message, err.stack);
});
}
},
getErrorQueue: function() {
return errorQueue;
},
clearErrors: function() {
errorQueue.length = 0;
const overlay = document.getElementById('__fallback_error_overlay__');
if (overlay && overlay.parentNode) {
document.body.removeChild(overlay);
fallbackUIShown = false;
}
},
// 新增:启用/禁用错误捕获
setErrorCaptureEnabled: function(enabled) {
errorCaptureEnabled = enabled;
console.log('%c[Error System] 错误捕获已' + (enabled ? '启用' : '禁用'), 'color: ' + (enabled ? '#52c41a' : '#faad14') + '; font-weight: bold;');
},
isErrorCaptureEnabled: function() {
return errorCaptureEnabled;
}
};
console.log('%c[Error System] 全局错误捕获已启用(增强版)', 'color: #52c41a; font-weight: bold;');
})();
</script>
<script>
// 判断是否为元素演示页面(在 body 加载后执行)
(function () {
if (document.body && window.location.pathname.includes('/components/')) {
document.body.classList.add('is-element-page');
} else if (!document.body) {
// 如果 body 还没加载,等待 DOMContentLoaded
document.addEventListener('DOMContentLoaded', function () {
if (window.location.pathname.includes('/components/')) {
document.body.classList.add('is-element-page');
}
});
}
})();
</script>
<div id="root"></div>
<!-- 加载自动调试客户端 -->
<script src="/auto-debug-client.js"></script>
<!-- 加载 bootstrap JS会自动挂载到 window.DevTemplateBootstrap -->
<script type="module" src="/assets/dev-template-bootstrap.js?v=1775123024591"></script>
<script type="module">
// 等待 bootstrap 加载完成
function waitForBootstrap() {
if (window.DevTemplateBootstrap) {
const { renderComponent, React, ReactDOM } = window.DevTemplateBootstrap;
// 将 React 和 ReactDOM 挂载到全局,让组件使用同一个实例
window.React = React;
window.ReactDOM = ReactDOM;
console.log('[Dev Template] Bootstrap 已就绪React 已挂载到全局');
// 动态导入组件(此时组件会使用 window.React
import('{{ENTRY}}').then(module => {
const Component = module.default;
if (!Component) {
const exportKeys = Object.keys(module);
throw new Error(`模块缺少默认导出${exportKeys.length > 0 ? `,当前导出: ${exportKeys.join(', ')}` : ''}`);
}
// 导出到全局(供调试使用)
window.AxhubDevComponent = Component;
console.log('[Dev Template] 组件默认导出已加载:', Component);
console.log('[Dev Template] 开始渲染');
// 渲染组件
renderComponent(Component);
}).catch(err => {
// 增强错误信息
const entryPath = '{{ENTRY}}';
let errorMessage = `组件加载失败: ${entryPath}`;
let errorDetails = [];
// 分析错误类型
if (err.message) {
if (err.message.includes('Failed to fetch')) {
errorDetails.push('• 可能原因:');
errorDetails.push(' 1. 组件文件不存在或路径错误');
errorDetails.push(' 2. 组件文件存在语法错误,导致编译失败');
errorDetails.push(' 3. 组件依赖的模块无法解析');
errorDetails.push(' 4. Vite 开发服务器未正确编译该文件');
errorDetails.push('');
errorDetails.push('• 建议检查:');
errorDetails.push(` 1. 确认文件存在: ${entryPath}`);
errorDetails.push(' 2. 查看浏览器 Network 面板,检查该文件的 HTTP 状态码');
errorDetails.push(' 3. 查看 Vite 开发服务器终端输出,是否有编译错误');
errorDetails.push(' 4. 尝试直接访问该文件 URL查看具体错误信息');
} else if (err.message.includes('does not provide an export') || err.message.includes('模块缺少默认导出')) {
errorDetails.push('• 错误原因:组件模块未按当前规范导出');
errorDetails.push('• 当前规范:组件文件必须使用 default export');
errorDetails.push('• 建议检查:');
errorDetails.push(' 1. 确认文件包含 `export default ...`');
errorDetails.push(' 2. 如果组件主体定义为 `const Component = ...`,请在文件末尾使用 `export default Component`');
errorDetails.push(' 3. 检查 export 语法是否正确,且没有被条件逻辑包裹');
} else {
errorDetails.push(`• 错误信息: ${err.message}`);
}
}
// 构建完整的错误信息
const fullErrorMessage = [
errorMessage,
'',
...errorDetails,
'',
'• 完整错误对象:',
JSON.stringify({
name: err.name,
message: err.message,
stack: err.stack
}, null, 2)
].join('\n');
console.error('[Dev Template] 组件加载失败:\n' + fullErrorMessage);
// 在页面上显示友好的错误提示
const root = document.getElementById('root');
if (root) {
root.innerHTML = `
<div style="padding: 40px; max-width: 800px; margin: 0 auto; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;">
<div style="background: #fff1f0; border: 1px solid #ffccc7; border-radius: 8px; padding: 24px;">
<div style="display: flex; align-items: center; gap: 12px; margin-bottom: 16px;">
<svg viewBox="64 64 896 896" width="24" height="24" fill="#ff4d4f">
<path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 0 1 0-96 48.01 48.01 0 0 1 0 96z"></path>
</svg>
<h2 style="margin: 0; color: #cf1322; font-size: 20px; font-weight: 600;">组件加载失败</h2>
</div>
<div style="background: white; border-radius: 4px; padding: 16px; margin-bottom: 16px;">
<div style="font-weight: 600; margin-bottom: 8px; color: #262626;">入口文件:</div>
<code style="display: block; padding: 8px 12px; background: #f5f5f5; border-radius: 4px; font-size: 13px; color: #d4380d; word-break: break-all;">${entryPath}</code>
</div>
${errorDetails.length > 0 ? `
<div style="background: white; border-radius: 4px; padding: 16px; margin-bottom: 16px;">
<pre style="margin: 0; font-size: 13px; line-height: 1.6; color: #595959; white-space: pre-wrap; word-break: break-word;">${errorDetails.join('\n')}</pre>
</div>
` : ''}
<details style="background: white; border-radius: 4px; padding: 16px;">
<summary style="cursor: pointer; font-weight: 600; color: #262626; user-select: none;">查看完整错误信息</summary>
<pre style="margin: 12px 0 0 0; padding: 12px; background: #f5f5f5; border-radius: 4px; font-size: 12px; color: #595959; overflow-x: auto; white-space: pre-wrap; word-break: break-all;">${JSON.stringify({
name: err.name,
message: err.message,
stack: err.stack
}, null, 2)}</pre>
</details>
<div style="margin-top: 16px; padding-top: 16px; border-top: 1px solid #ffccc7;">
<div style="font-size: 13px; color: #8c8c8c;">
💡 提示:打开浏览器开发者工具的 Network 和 Console 面板查看更多信息
</div>
</div>
</div>
</div>
`;
}
});
} else {
setTimeout(waitForBootstrap, 10);
}
}
waitForBootstrap();
</script>
</body>
</html>

View File

@@ -0,0 +1,106 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>{{TITLE}}</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<style>
/* 全局样式 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
width: 100%;
min-height: 100%;
height: 100%;
overflow-x: hidden;
overflow-y: auto;
}
#root {
width: 100%;
margin-left: auto;
margin-right: auto;
height: 100%;
min-height: 100vh;
overflow: visible;
}
/* 如果是元素演示页面(可能被内嵌到 iframe设置固定尺寸 */
body.is-element-page #root {
width: 100vw;
height: 100vh;
}
/* 平板和手机模式隐藏滚动条 */
@media (max-width: 1024px) {
::-webkit-scrollbar {
display: none;
}
html,
body {
scrollbar-width: none;
/* Firefox */
}
}
</style>
</head>
<body>
<script>
// 判断是否为元素演示页面(在 body 加载后执行)
(function () {
if (document.body && window.location.pathname.includes('/components/')) {
document.body.classList.add('is-element-page');
} else if (!document.body) {
// 如果 body 还没加载,等待 DOMContentLoaded
document.addEventListener('DOMContentLoaded', function () {
if (window.location.pathname.includes('/components/')) {
document.body.classList.add('is-element-page');
}
});
}
})();
</script>
<div id="root"></div>
<!-- 加载 bootstrap JS会自动挂载到 window.HtmlTemplateBootstrap -->
<script type="module" src="{{BOOTSTRAP_PATH}}"></script>
<!-- 加载组件 JSIIFE 格式,会挂载 UserComponent 到 window -->
<script src="{{ENTRY}}"></script>
<script type="module">
// 等待 bootstrap 和组件加载完成
function waitForReady() {
if (window.HtmlTemplateBootstrap && window.UserComponent) {
const { renderComponent, React, ReactDOM } = window.HtmlTemplateBootstrap;
// 将 React 和 ReactDOM 挂载到全局
window.React = React;
window.ReactDOM = ReactDOM;
// 获取组件
const Component = window.UserComponent.Component || window.UserComponent.default || window.UserComponent;
// 渲染组件
renderComponent(Component);
} else {
setTimeout(waitForReady, 10);
}
}
waitForReady();
</script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 745 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 795 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

191
axhub-make/admin/index.html Normal file
View File

@@ -0,0 +1,191 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/x-icon" href="/assets/favicon.ico?v=1775123024591">
<title>未命名项目 - Axhub Make</title>
<style>
body {
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
background: linear-gradient(135deg, #dfe4f7 0%, #c7c4e8 100%);
min-height: 100vh;
}
.app-container {
max-width: 1400px;
margin: 0 auto;
padding: 40px 24px;
}
.header {
text-align: center;
margin-bottom: 40px;
}
.header-title {
color: #333;
font-size: 2.5rem;
font-weight: 600;
margin: 0 0 12px 0;
text-shadow: 2px 2px 8px rgba(0, 0, 0, 0.2);
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
}
.header-subtitle {
color: #999;
font-size: 1rem;
margin: 0;
}
.main-card {
background: white;
border-radius: 12px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15);
overflow: hidden;
padding: 24px;
}
.api-info-banner {
background: linear-gradient(135deg, #e6f7ff 0%, #bae7ff 100%);
border-bottom: 1px solid #91d5ff;
padding: 16px 24px;
display: flex;
align-items: center;
gap: 12px;
}
.api-info-banner svg {
width: 20px;
height: 20px;
color: #1890ff;
}
.api-info-text {
color: #0050b3;
font-size: 14px;
}
.api-link {
color: #1890ff;
text-decoration: none;
font-weight: 500;
margin-left: 8px;
}
.api-link:hover {
text-decoration: underline;
}
.item-name-cell {
font-weight: 500;
color: #262626;
}
.item-key {
color: #8c8c8c;
font-size: 13px;
font-weight: 400;
margin-left: 8px;
}
.action-buttons {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
/* Ant Design overrides
.ant-tabs {
padding: 0 24px;
}
.ant-tabs-nav {
margin-bottom: 0 !important;
}
.ant-tabs-tab {
font-size: 16px;
padding: 12px 0;
}
.ant-tabs-content-holder {
padding: 24px;
}
.ant-table {
font-size: 14px;
}
.ant-btn {
font-size: 14px;
}
.ant-empty {
padding: 60px 0;
}
.ant-badge-count {
background: #1890ff;
} */
.loading-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 400px;
}
/* 表格优化 */
/* .ant-table-wrapper {
overflow-x: auto;
}
.ant-table-cell {
vertical-align: middle !important;
} */
/* 封面图片悬停效果 */
.cover-image-container {
transition: transform 0.2s ease;
}
.cover-image-container:hover {
transform: scale(1.05);
}
/* 代码样式 */
code {
background: #f5f5f5;
padding: 2px 6px;
border-radius: 3px;
font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
}
</style>
<script type="module" crossorigin src="/assets/index.js?v=1775123024591"></script>
<link rel="modulepreload" crossorigin href="/assets/chunks/_commonjsHelpers.js?v=1775123024591">
<link rel="modulepreload" crossorigin href="/assets/chunks/vendor-react.js?v=1775123024591">
<link rel="modulepreload" crossorigin href="/assets/chunks/preload-helper.js?v=1775123024591">
<link rel="modulepreload" crossorigin href="/assets/chunks/_commonjs-dynamic-modules.js?v=1775123024591">
<link rel="modulepreload" crossorigin href="/assets/chunks/vendor-common.js?v=1775123024591">
<link rel="modulepreload" crossorigin href="/assets/chunks/AppDialogProvider.js?v=1775123024591">
<link rel="modulepreload" crossorigin href="/assets/chunks/vendor-antd.js?v=1775123024591">
<link rel="modulepreload" crossorigin href="/assets/chunks/vendor-assistant.js?v=1775123024591">
<link rel="modulepreload" crossorigin href="/assets/chunks/use-feedback-bridge.js?v=1775123024591">
<link rel="modulepreload" crossorigin href="/assets/chunks/vendor-export.js?v=1775123024591">
<link rel="stylesheet" crossorigin href="/assets/AppDialogProvider.css?v=1775123024591">
<link rel="stylesheet" crossorigin href="/assets/index.css?v=1775123024591">
</head>
<body>
<div id="root"></div>
</body>
</html>

View File

@@ -0,0 +1,217 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>{{TITLE}} - Spec</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="/assets/spec-template-vendor.css?v=1775123024591">
<link rel="stylesheet" href="/assets/spec-template-bootstrap.css?v=1775123024591">
<style>
/* 全局样式 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
width: 100%;
min-height: 100%;
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
line-height: 1.6;
color: #333;
background: #f5f5f5;
}
#spec-root {
width: 100%;
min-height: 100vh;
}
/* 加载状态 */
.loading {
padding: 60px 20px;
color: #8c8c8c;
}
.loading::after {
content: '...';
animation: loading 1.5s infinite;
}
@keyframes loading {
0%, 20% {
content: '.';
}
40% {
content: '..';
}
60%, 100% {
content: '...';
}
}
/* 错误状态 */
.error {
color: #ff4d4f;
padding: 20px;
background: #fff2f0;
border: 1px solid #ffccc7;
border-radius: 4px;
margin: 20px;
}
.error h2 {
margin-top: 0;
}
</style>
</head>
<body>
<div id="spec-root" class="loading">加载中</div>
<!-- 自定义模板不会走 Vite 默认 index.html 注入链路,这里手动补齐 HMR client 和 React preamble。 -->
<script type="module">
import '/@vite/client';
import '@vitejs/plugin-react/preamble';
</script>
<!-- 加载 bootstrap JS会自动挂载到 window.SpecTemplateBootstrap -->
<script type="module" src="/assets/spec-template-bootstrap.js?v=1775123024591"></script>
<script type="module">
const specTemplateState = {
mode: 'single',
currentSpecUrl: '',
currentDocsConfig: [],
currentDocUrls: []
};
function setSpecTemplateState(nextState) {
specTemplateState.mode = nextState.mode;
specTemplateState.currentSpecUrl = nextState.currentSpecUrl || '';
specTemplateState.currentDocsConfig = Array.isArray(nextState.currentDocsConfig) ? nextState.currentDocsConfig : [];
specTemplateState.currentDocUrls = Array.isArray(nextState.currentDocUrls) ? nextState.currentDocUrls : [];
}
async function reloadCurrentSpecDocuments() {
if (!window.SpecTemplateBootstrap) {
return;
}
if (specTemplateState.mode === 'multi' && specTemplateState.currentDocsConfig.length > 0) {
await window.SpecTemplateBootstrap.loadMarkdownDocumentsFromUrls(specTemplateState.currentDocsConfig);
return;
}
if (specTemplateState.currentSpecUrl) {
await window.SpecTemplateBootstrap.loadMarkdownFromUrl(specTemplateState.currentSpecUrl);
}
}
if (import.meta.hot) {
import.meta.hot.on('axhub:spec-doc-update', async (payload) => {
if (!payload?.docUrl || !specTemplateState.currentDocUrls.includes(payload.docUrl)) {
return;
}
console.log('[Spec Template] 检测到文档热更新,重新加载:', payload.docUrl);
try {
await reloadCurrentSpecDocuments();
} catch (error) {
console.error('[Spec Template] 热更新重载失败:', error);
}
});
}
function decodeHtmlEntities(str) {
const textarea = document.createElement('textarea');
textarea.innerHTML = str;
return textarea.value;
}
function waitForBootstrap() {
if (window.SpecTemplateBootstrap) {
console.log('[Spec Template] Bootstrap 已就绪');
console.log('[Spec Template] 可用方法:', Object.keys(window.SpecTemplateBootstrap));
const isMultiDoc = '{{MULTI_DOC}}' === 'true';
const docsConfigStr = decodeHtmlEntities('{{DOCS_CONFIG}}');
console.log('[Spec Template] 多文档模式:', isMultiDoc);
console.log('[Spec Template] 文档配置字符串:', docsConfigStr);
if (isMultiDoc && docsConfigStr && docsConfigStr !== '[]') {
try {
const docsConfig = JSON.parse(docsConfigStr);
console.log('[Spec Template] 多文档模式,加载文档:', docsConfig);
setSpecTemplateState({
mode: 'multi',
currentDocsConfig: docsConfig,
currentDocUrls: docsConfig.map((item) => item.url)
});
if (window.SpecTemplateBootstrap.loadMarkdownDocumentsFromUrls) {
window.SpecTemplateBootstrap.loadMarkdownDocumentsFromUrls(docsConfig).catch(err => {
console.error('[Spec Template] 加载失败:', err);
});
} else {
console.error('[Spec Template] loadMarkdownDocumentsFromUrls 方法不存在');
}
} catch (err) {
console.error('[Spec Template] 解析文档配置失败:', err);
console.error('[Spec Template] 原始字符串:', docsConfigStr);
const urlParams = new URLSearchParams(window.location.search);
const specUrl = urlParams.get('url') || '{{SPEC_URL}}';
console.log('[Spec Template] 降级到单文档模式,加载 Spec:', specUrl);
setSpecTemplateState({
mode: 'single',
currentSpecUrl: specUrl,
currentDocUrls: specUrl ? [specUrl] : []
});
// 检查是否是有效的 URL不是占位符
if (specUrl && !specUrl.includes('{{') && !specUrl.includes('}}') && window.SpecTemplateBootstrap.loadMarkdownFromUrl) {
window.SpecTemplateBootstrap.loadMarkdownFromUrl(specUrl).catch(err => {
console.error('[Spec Template] 加载失败:', err);
});
}
}
} else {
const urlParams = new URLSearchParams(window.location.search);
const specUrl = urlParams.get('url') || '{{SPEC_URL}}';
console.log('[Spec Template] 单文档模式,加载 Spec:', specUrl);
setSpecTemplateState({
mode: 'single',
currentSpecUrl: specUrl,
currentDocUrls: specUrl ? [specUrl] : []
});
// 检查是否是有效的 URL不是占位符
if (specUrl && !specUrl.includes('{{') && !specUrl.includes('}}')) {
console.log('[Spec Template] 开始加载文档');
window.SpecTemplateBootstrap.loadMarkdownFromUrl(specUrl).catch(err => {
console.error('[Spec Template] 加载失败:', err);
});
} else {
console.error('[Spec Template] 没有有效的文档 URL, specUrl:', specUrl);
}
}
} else {
setTimeout(waitForBootstrap, 10);
}
}
waitForBootstrap();
</script>
</body>
</html>