Initial commit: Memind H5 portal with MindSpace, Plaza, and agent jobs.

Track application source and tests; exclude local env, user workspaces, and runtime data via .gitignore.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
John
2026-06-15 15:04:43 -07:00
commit 2e14873f2d
272 changed files with 64133 additions and 0 deletions
+298
View File
@@ -0,0 +1,298 @@
export const MINDSPACE_PAGE_CONTENT_MESSAGE = 'mindspace:page-content';
const EDITOR_STYLE = `<style id="mindspace-visual-editor-style">
[data-mindspace-editing="true"] [contenteditable="true"] {
outline: 1px dashed rgba(61, 139, 253, 0.45);
outline-offset: 2px;
cursor: text;
}
[data-mindspace-editing="true"] [contenteditable="true"]:focus {
outline: 2px solid rgba(61, 139, 253, 0.95);
background: rgba(61, 139, 253, 0.06);
}
[data-mindspace-editing="true"] img {
cursor: pointer;
outline: 1px dashed transparent;
outline-offset: 2px;
transition: outline-color 0.15s ease;
}
[data-mindspace-editing="true"] img:hover {
outline-color: rgba(238, 176, 78, 0.95);
}
[data-mindspace-editing="true"] [data-mindspace-bg-target="true"] {
cursor: copy;
}
[data-mindspace-editing="true"] [data-mindspace-bg-target="true"]:hover {
box-shadow: inset 0 0 0 2px rgba(238, 176, 78, 0.45);
}
.mindspace-editor-popover {
position: fixed;
z-index: 2147483646;
width: min(280px, calc(100vw - 24px));
padding: 12px;
border: 1px solid rgba(24, 33, 29, 0.14);
border-radius: 12px;
background: rgba(255, 252, 244, 0.98);
color: #18211d;
box-shadow: 0 18px 48px rgba(24, 33, 29, 0.18);
font: 12px/1.45 -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
.mindspace-editor-popover p {
margin: 0 0 8px;
font-size: 13px;
font-weight: 700;
}
.mindspace-editor-popover label {
display: grid;
gap: 4px;
margin-bottom: 8px;
}
.mindspace-editor-popover input[type='url'],
.mindspace-editor-popover input[type='text'],
.mindspace-editor-popover input[type='color'],
.mindspace-editor-popover input[type='file'] {
width: 100%;
padding: 7px 8px;
border: 1px solid rgba(24, 33, 29, 0.14);
border-radius: 8px;
background: #fff;
color: inherit;
font: inherit;
}
.mindspace-editor-popover-actions {
display: flex;
justify-content: flex-end;
gap: 8px;
}
.mindspace-editor-popover-actions button {
border: 0;
border-radius: 999px;
padding: 6px 12px;
cursor: pointer;
font: inherit;
}
.mindspace-editor-popover-actions button[data-action='apply'] {
color: #fffaf0;
background: #18211d;
}
.mindspace-editor-popover-actions button[data-action='cancel'] {
color: #52605a;
background: rgba(24, 33, 29, 0.08);
}
</style>`;
const EDITOR_SCRIPT = `<script id="mindspace-visual-editor-script">
(function () {
var MESSAGE = ${JSON.stringify(MINDSPACE_PAGE_CONTENT_MESSAGE)};
var TEXT_SELECTOR = 'h1,h2,h3,h4,h5,h6,p,li,td,th,figcaption,blockquote,span,a,label,small,strong,em,.sub,.tagline';
var popover = null;
var emitTimer = null;
function debounceEmit() {
if (emitTimer) window.clearTimeout(emitTimer);
emitTimer = window.setTimeout(emitChange, 350);
}
function stripEditorArtifacts(root) {
root.querySelectorAll('#mindspace-visual-editor-style,#mindspace-visual-editor-script,.mindspace-editor-popover').forEach(function (node) {
node.remove();
});
root.querySelectorAll('[contenteditable]').forEach(function (node) {
node.removeAttribute('contenteditable');
});
root.querySelectorAll('[data-mindspace-bg-target]').forEach(function (node) {
node.removeAttribute('data-mindspace-bg-target');
});
if (root.body) root.body.removeAttribute('data-mindspace-editing');
}
function serializeDocument() {
var clone = document.documentElement.cloneNode(true);
stripEditorArtifacts(clone);
var doctype = document.doctype;
var prefix = doctype
? '<!DOCTYPE ' + doctype.name + (doctype.publicId ? ' PUBLIC "' + doctype.publicId + '"' : '') + (doctype.systemId ? ' "' + doctype.systemId + '"' : '') + '>'
: '<!DOCTYPE html>';
return prefix + '\\n' + clone.outerHTML;
}
function emitChange() {
parent.postMessage({ type: MESSAGE, html: serializeDocument() }, '*');
}
function closePopover() {
if (popover && popover.parentNode) popover.parentNode.removeChild(popover);
popover = null;
}
function openPopover(title, fieldsHtml, onApply, anchor) {
closePopover();
popover = document.createElement('div');
popover.className = 'mindspace-editor-popover';
popover.innerHTML =
'<p>' + title + '</p>' +
fieldsHtml +
'<div class="mindspace-editor-popover-actions">' +
'<button type="button" data-action="cancel">取消</button>' +
'<button type="button" data-action="apply">应用</button>' +
'</div>';
document.body.appendChild(popover);
var rect = anchor.getBoundingClientRect();
var top = Math.min(rect.bottom + 8, window.innerHeight - popover.offsetHeight - 8);
var left = Math.min(rect.left, window.innerWidth - popover.offsetWidth - 8);
popover.style.top = Math.max(8, top) + 'px';
popover.style.left = Math.max(8, left) + 'px';
popover.querySelector('[data-action="cancel"]').addEventListener('click', closePopover);
popover.querySelector('[data-action="apply"]').addEventListener('click', function () {
var pending = onApply(popover);
closePopover();
if (pending !== 'async') emitChange();
});
}
function readFileAsDataUrl(file, callback) {
var reader = new FileReader();
reader.onload = function () { callback(String(reader.result || '')); };
reader.readAsDataURL(file);
}
function openImageEditor(img) {
openPopover(
'替换图片',
'<label>图片链接<input type="url" data-role="url" value="' + (img.src.indexOf('data:') === 0 ? '' : img.src.replace(/"/g, '&quot;')) + '" placeholder="https://..." /></label>' +
'<label>本地图片<input type="file" accept="image/*" data-role="file" /></label>',
function (panel) {
var fileInput = panel.querySelector('[data-role="file"]');
var urlInput = panel.querySelector('[data-role="url"]');
if (fileInput.files && fileInput.files[0]) {
readFileAsDataUrl(fileInput.files[0], function (dataUrl) {
img.src = dataUrl;
emitChange();
});
return 'async';
}
var next = String(urlInput.value || '').trim();
if (next) img.src = next;
},
img,
);
}
function rgbToHex(color) {
var match = String(color || '').match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/i);
if (!match) return '#ffffff';
function hex(n) { return Number(n).toString(16).padStart(2, '0'); }
return '#' + hex(match[1]) + hex(match[2]) + hex(match[3]);
}
function openBackgroundEditor(el) {
var style = window.getComputedStyle(el);
openPopover(
'替换背景',
'<label>背景颜色<input type="color" data-role="color" value="' + rgbToHex(style.backgroundColor) + '" /></label>' +
'<label>背景图片链接<input type="url" data-role="bg-url" placeholder="留空则仅使用颜色" /></label>' +
'<label>本地背景图<input type="file" accept="image/*" data-role="bg-file" /></label>',
function (panel) {
var color = panel.querySelector('[data-role="color"]').value;
var bgUrl = String(panel.querySelector('[data-role="bg-url"]').value || '').trim();
var bgFile = panel.querySelector('[data-role="bg-file"]');
function applyBackground(url) {
if (url) {
el.style.backgroundImage = 'url("' + url.replace(/"/g, '\\"') + '")';
el.style.backgroundSize = el.style.backgroundSize || 'cover';
el.style.backgroundPosition = el.style.backgroundPosition || 'center';
} else {
el.style.backgroundImage = 'none';
}
if (color) el.style.backgroundColor = color;
}
if (bgFile.files && bgFile.files[0]) {
readFileAsDataUrl(bgFile.files[0], function (url) {
applyBackground(url);
emitChange();
});
return 'async';
}
applyBackground(bgUrl);
},
el,
);
}
function markBackgroundTargets() {
var nodes = document.querySelectorAll('body, main, section, article, header, footer, div');
nodes.forEach(function (el) {
if (el.closest('.mindspace-editor-popover')) return;
var style = window.getComputedStyle(el);
var cls = String(el.className || '');
var structural = /\\b(page|hero|cover|banner|bg|background|section|card|panel|wrap|container)\\b/i.test(cls);
var painted = style.backgroundImage !== 'none' || (style.backgroundColor && style.backgroundColor !== 'rgba(0, 0, 0, 0)' && style.backgroundColor !== 'transparent');
if (el.tagName === 'BODY' || structural || (painted && el.offsetWidth > 64 && el.offsetHeight > 64)) {
el.setAttribute('data-mindspace-bg-target', 'true');
}
});
}
function enableTextEditing() {
document.querySelectorAll(TEXT_SELECTOR).forEach(function (el) {
if (el.closest('.mindspace-editor-popover')) return;
if (el.querySelector && el.querySelector('img,video,iframe,svg,canvas')) return;
el.setAttribute('contenteditable', 'true');
el.setAttribute('spellcheck', 'true');
});
}
function onClick(event) {
var target = event.target;
if (!(target instanceof Element)) return;
if (target.closest('.mindspace-editor-popover')) return;
if (target.tagName === 'IMG') {
event.preventDefault();
openImageEditor(target);
return;
}
if (target.isContentEditable) return;
if (target.matches('[data-mindspace-bg-target="true"]') || target.tagName === 'BODY') {
event.preventDefault();
openBackgroundEditor(target.tagName === 'BODY' ? document.body : target);
}
}
function init() {
document.body.setAttribute('data-mindspace-editing', 'true');
enableTextEditing();
markBackgroundTargets();
document.addEventListener('input', debounceEmit);
document.addEventListener('blur', debounceEmit, true);
document.addEventListener('click', onClick, true);
document.addEventListener('keydown', function (event) {
if (event.key === 'Escape') closePopover();
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
</script>`;
export function buildEditablePreviewDocument(html: string): string {
const source = String(html ?? '').trim();
const injection = `${EDITOR_STYLE}${EDITOR_SCRIPT}`;
if (!source) {
return buildEditablePreviewDocument(
'<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"></head><body><p>空白页面</p></body></html>',
);
}
if (/<\/body>/i.test(source)) {
return source.replace(/<\/body>/i, `${injection}</body>`);
}
if (/<\/html>/i.test(source)) {
return source.replace(/<\/html>/i, `${injection}</html>`);
}
return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"></head><body>${source}${injection}</body></html>`;
}