git save current version as 0.0.7
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
Binary file not shown.
Binary file not shown.
@@ -42,8 +42,12 @@
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.1.1",
|
||||
"fast-xml-parser": "^4.5.3",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"jszip": "^3.10.1",
|
||||
"lucide-react": "^0.554.0",
|
||||
"next": "16.0.3",
|
||||
"papaparse": "^5.5.3",
|
||||
"react": "19.2.0",
|
||||
"react-dom": "19.2.0",
|
||||
"react-dropzone": "^14.3.8",
|
||||
|
||||
Generated
+30
@@ -98,12 +98,24 @@ importers:
|
||||
cmdk:
|
||||
specifier: ^1.1.1
|
||||
version: 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.5))(@types/react@19.2.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
fast-xml-parser:
|
||||
specifier: ^4.5.3
|
||||
version: 4.5.3
|
||||
iconv-lite:
|
||||
specifier: ^0.6.3
|
||||
version: 0.6.3
|
||||
jszip:
|
||||
specifier: ^3.10.1
|
||||
version: 3.10.1
|
||||
lucide-react:
|
||||
specifier: ^0.554.0
|
||||
version: 0.554.0(react@19.2.0)
|
||||
next:
|
||||
specifier: 16.0.3
|
||||
version: 16.0.3(@babel/core@7.28.5)(@playwright/test@1.57.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
papaparse:
|
||||
specifier: ^5.5.3
|
||||
version: 5.5.3
|
||||
react:
|
||||
specifier: 19.2.0
|
||||
version: 19.2.0
|
||||
@@ -2927,6 +2939,10 @@ packages:
|
||||
fast-levenshtein@2.0.6:
|
||||
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
|
||||
|
||||
fast-xml-parser@4.5.3:
|
||||
resolution: {integrity: sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==}
|
||||
hasBin: true
|
||||
|
||||
fastq@1.19.1:
|
||||
resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
|
||||
|
||||
@@ -3935,6 +3951,9 @@ packages:
|
||||
pako@1.0.11:
|
||||
resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
|
||||
|
||||
papaparse@5.5.3:
|
||||
resolution: {integrity: sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A==}
|
||||
|
||||
parchment@3.0.0:
|
||||
resolution: {integrity: sha512-HUrJFQ/StvgmXRcQ1ftY6VEZUq3jA2t9ncFN4F84J/vN0/FPpQF+8FKXb3l6fLces6q0uOHj6NJn+2xvZnxO6A==}
|
||||
|
||||
@@ -4547,6 +4566,9 @@ packages:
|
||||
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
strnum@1.1.2:
|
||||
resolution: {integrity: sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==}
|
||||
|
||||
styled-jsx@5.1.6:
|
||||
resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
@@ -8053,6 +8075,10 @@ snapshots:
|
||||
|
||||
fast-levenshtein@2.0.6: {}
|
||||
|
||||
fast-xml-parser@4.5.3:
|
||||
dependencies:
|
||||
strnum: 1.1.2
|
||||
|
||||
fastq@1.19.1:
|
||||
dependencies:
|
||||
reusify: 1.1.0
|
||||
@@ -9393,6 +9419,8 @@ snapshots:
|
||||
|
||||
pako@1.0.11: {}
|
||||
|
||||
papaparse@5.5.3: {}
|
||||
|
||||
parchment@3.0.0: {}
|
||||
|
||||
parent-module@1.0.1:
|
||||
@@ -10195,6 +10223,8 @@ snapshots:
|
||||
|
||||
strip-json-comments@3.1.1: {}
|
||||
|
||||
strnum@1.1.2: {}
|
||||
|
||||
styled-jsx@5.1.6(@babel/core@7.28.5)(react@19.2.0):
|
||||
dependencies:
|
||||
client-only: 0.0.1
|
||||
|
||||
@@ -0,0 +1,303 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import JSZip from 'jszip';
|
||||
import { XMLParser } from 'fast-xml-parser';
|
||||
import Papa from 'papaparse';
|
||||
import iconv from 'iconv-lite';
|
||||
import { normalizeSnapshot } from '@/lib/mindmap/snapshot';
|
||||
import type { MindmapNode, MindmapSnapshot } from '@/types/mindmap';
|
||||
|
||||
const arrify = <T>(value: T | T[] | undefined | null): T[] => {
|
||||
if (Array.isArray(value)) return value;
|
||||
if (value === undefined || value === null) return [];
|
||||
return [value];
|
||||
};
|
||||
|
||||
const safeText = (value: unknown, fallback = '未命名节点') => {
|
||||
if (typeof value === 'string' && value.trim()) return value.trim();
|
||||
if (value && typeof value === 'object' && 'PlainText' in value) {
|
||||
const maybe = (value as { PlainText?: string }).PlainText;
|
||||
if (typeof maybe === 'string' && maybe.trim()) return maybe.trim();
|
||||
}
|
||||
return fallback;
|
||||
};
|
||||
|
||||
const pickUid = (input: Record<string, unknown>) => {
|
||||
const candidates = ['OId', 'Guid', 'ID', 'Id', 'uid'];
|
||||
for (const key of candidates) {
|
||||
const value = input[key];
|
||||
if (typeof value === 'string' && value.trim()) return value.trim();
|
||||
}
|
||||
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
||||
return crypto.randomUUID();
|
||||
}
|
||||
return `node_${Date.now()}_${Math.random().toString(16).slice(2, 8)}`;
|
||||
};
|
||||
|
||||
const decodeBuffer = (buffer: Buffer) => {
|
||||
const utf8 = buffer.toString('utf8');
|
||||
const bad = (utf8.match(/\ufffd/g) ?? []).length;
|
||||
if (bad / Math.max(utf8.length, 1) <= 0.01) {
|
||||
return utf8;
|
||||
}
|
||||
try {
|
||||
return iconv.decode(buffer, 'gb18030');
|
||||
} catch {
|
||||
return utf8;
|
||||
}
|
||||
};
|
||||
|
||||
const parseUri = (input: unknown): string | null => {
|
||||
if (typeof input === 'string') {
|
||||
return input.trim();
|
||||
}
|
||||
if (
|
||||
input &&
|
||||
typeof input === 'object' &&
|
||||
'#text' in (input as Record<string, unknown>) &&
|
||||
typeof (input as Record<string, unknown>)['#text'] === 'string'
|
||||
) {
|
||||
return ((input as Record<string, unknown>)['#text'] as string).trim();
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const convertMmapTopic = (
|
||||
raw: any,
|
||||
uriDataMap?: Map<string, { url: string; width?: number; height?: number }>
|
||||
): MindmapNode => {
|
||||
const data: MindmapNode['data'] = {
|
||||
text: safeText(raw?.Text ?? raw?.text, ''),
|
||||
};
|
||||
if (raw?.Notes?.PlainText) {
|
||||
data.note = String(raw.Notes.PlainText);
|
||||
}
|
||||
const hyperlink =
|
||||
raw?.Hyperlink?.Url ??
|
||||
raw?.Hyperlink?.URL ??
|
||||
raw?.Hyperlink?.link ??
|
||||
raw?.hyperlink?.url;
|
||||
if (typeof hyperlink === 'string' && hyperlink.trim()) {
|
||||
data.hyperlink = hyperlink.trim();
|
||||
}
|
||||
if (raw?.Labels?.Label) {
|
||||
const tags = arrify(raw.Labels.Label)
|
||||
.map((item) => (typeof item === 'string' ? item.trim() : ''))
|
||||
.filter(Boolean);
|
||||
if (tags.length > 0) data.tag = tags;
|
||||
}
|
||||
|
||||
const images = [...arrify(raw?.OneImage), ...arrify(raw?.Image)];
|
||||
if (images.length > 0) {
|
||||
const first = images[0];
|
||||
const uri = parseUri(
|
||||
first?.Image?.ImageData?.Uri ??
|
||||
first?.ImageData?.Uri ??
|
||||
first?.Uri
|
||||
);
|
||||
const size =
|
||||
first?.Image?.ImageSize ??
|
||||
first?.ImageSize;
|
||||
if (uri && uriDataMap?.has(uri)) {
|
||||
const payload = uriDataMap.get(uri)!;
|
||||
data.image = payload.url;
|
||||
if (size?.Width || size?.Height) {
|
||||
data.imageSize = {
|
||||
width: Number(size.Width) || 0,
|
||||
height: Number(size.Height) || 0,
|
||||
custom: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!data.text && images.length === 0) {
|
||||
data.text = '未命名节点';
|
||||
}
|
||||
|
||||
const childrenRaw =
|
||||
raw?.SubTopics?.Topic ??
|
||||
raw?.Topics?.Topic ??
|
||||
raw?.SubTopics?.oneTopic ??
|
||||
raw?.Topic;
|
||||
const children = arrify(childrenRaw).map((child) =>
|
||||
convertMmapTopic(child, uriDataMap)
|
||||
);
|
||||
|
||||
return {
|
||||
uid: pickUid(raw ?? {}),
|
||||
data,
|
||||
children,
|
||||
};
|
||||
};
|
||||
|
||||
const parseMmap = async (buffer: Buffer): Promise<MindmapSnapshot> => {
|
||||
const zip = await JSZip.loadAsync(buffer);
|
||||
const docEntry =
|
||||
zip.file('Document.xml') ??
|
||||
zip.file('document.xml') ??
|
||||
zip.file(/Document\.xml$/i);
|
||||
const target = Array.isArray(docEntry) ? docEntry[0] : docEntry;
|
||||
if (!target) {
|
||||
throw new Error('未找到 Document.xml');
|
||||
}
|
||||
const xmlText = await target.async('text');
|
||||
const parser = new XMLParser({
|
||||
ignoreAttributes: false,
|
||||
removeNSPrefix: true,
|
||||
attributeNamePrefix: '',
|
||||
});
|
||||
const parsed = parser.parse(xmlText);
|
||||
const topicRoot =
|
||||
parsed?.Map?.OneTopic?.Topic ??
|
||||
parsed?.OneTopic?.Topic ??
|
||||
parsed?.Topic ??
|
||||
null;
|
||||
const rootTopic = Array.isArray(topicRoot) ? topicRoot[0] : topicRoot;
|
||||
if (!rootTopic) {
|
||||
throw new Error('mmap 文件中未找到 Topic 节点');
|
||||
}
|
||||
|
||||
const uriSet = new Set<string>();
|
||||
const collectImageUris = (node: any) => {
|
||||
if (!node || typeof node !== 'object') return;
|
||||
const images = [...arrify(node.OneImage), ...arrify(node.Image)];
|
||||
images.forEach((img) => {
|
||||
const uri = parseUri(
|
||||
img?.Image?.ImageData?.Uri ??
|
||||
img?.ImageData?.Uri ??
|
||||
img?.Uri
|
||||
);
|
||||
if (uri) {
|
||||
uriSet.add(uri);
|
||||
}
|
||||
});
|
||||
const children =
|
||||
arrify(node.SubTopics?.Topic) ??
|
||||
arrify(node.Topics?.Topic) ??
|
||||
arrify(node.Topic);
|
||||
children.forEach(collectImageUris);
|
||||
};
|
||||
collectImageUris(rootTopic);
|
||||
|
||||
const uriDataMap = new Map<string, { url: string; width?: number; height?: number }>();
|
||||
const detectMime = (buf: Buffer) => {
|
||||
if (buf.slice(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]))) {
|
||||
return 'image/png';
|
||||
}
|
||||
if (buf.slice(0, 3).equals(Buffer.from([0xff, 0xd8, 0xff]))) {
|
||||
return 'image/jpeg';
|
||||
}
|
||||
if (buf.slice(0, 2).equals(Buffer.from([0x42, 0x4d]))) {
|
||||
return 'image/bmp';
|
||||
}
|
||||
return 'application/octet-stream';
|
||||
};
|
||||
|
||||
for (const uri of uriSet) {
|
||||
const path = uri.startsWith('mmarch://') ? uri.replace('mmarch://', '') : uri;
|
||||
const file = zip.file(path);
|
||||
if (!file) continue;
|
||||
const bin = await file.async('nodebuffer');
|
||||
const mime = detectMime(bin);
|
||||
const base64 = bin.toString('base64');
|
||||
uriDataMap.set(uri, { url: `data:${mime};base64,${base64}` });
|
||||
}
|
||||
|
||||
const rootNode = convertMmapTopic(rootTopic, uriDataMap);
|
||||
return {
|
||||
version: Date.now(),
|
||||
layout: 'logicalStructure',
|
||||
root: rootNode,
|
||||
theme: { template: 'default', config: {} },
|
||||
view: null,
|
||||
};
|
||||
};
|
||||
|
||||
const parseCsv = (buffer: Buffer): MindmapSnapshot => {
|
||||
const text = decodeBuffer(buffer);
|
||||
const { data, errors } = Papa.parse<string[]>(text, {
|
||||
skipEmptyLines: true,
|
||||
});
|
||||
if (errors && errors.length > 0) {
|
||||
console.warn('CSV parse warnings', errors);
|
||||
}
|
||||
const rows = (data as string[][]).filter((row) => Array.isArray(row));
|
||||
let root: MindmapNode | null = null;
|
||||
const stack: MindmapNode[] = [];
|
||||
|
||||
rows.forEach((row) => {
|
||||
const idx = row.findIndex((cell) => typeof cell === 'string' && cell.trim());
|
||||
if (idx === -1) return;
|
||||
const textValue = String(row[idx]).trim();
|
||||
const node: MindmapNode = {
|
||||
uid: pickUid({}),
|
||||
data: { text: textValue },
|
||||
children: [],
|
||||
};
|
||||
stack[idx] = node;
|
||||
stack.length = idx + 1;
|
||||
|
||||
if (idx === 0) {
|
||||
if (!root) {
|
||||
root = node;
|
||||
} else {
|
||||
(root.children ??= []).push(node);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const parent = stack[idx - 1] ?? root;
|
||||
if (parent) {
|
||||
(parent.children ??= []).push(node);
|
||||
} else {
|
||||
root = node;
|
||||
}
|
||||
});
|
||||
|
||||
if (!root) {
|
||||
root = {
|
||||
uid: pickUid({}),
|
||||
data: { text: '导入导图' },
|
||||
children: [],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
version: Date.now(),
|
||||
layout: 'logicalStructure',
|
||||
root,
|
||||
theme: { template: 'default', config: {} },
|
||||
view: null,
|
||||
};
|
||||
};
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const formData = await request.formData();
|
||||
const file = formData.get('file');
|
||||
if (!(file instanceof File)) {
|
||||
return NextResponse.json({ error: '缺少文件' }, { status: 400 });
|
||||
}
|
||||
const buffer = Buffer.from(await file.arrayBuffer());
|
||||
const ext = file.name.split('.').pop()?.toLowerCase() ?? '';
|
||||
|
||||
let snapshot: MindmapSnapshot;
|
||||
if (ext === 'mmap') {
|
||||
snapshot = await parseMmap(buffer);
|
||||
} else if (ext === 'csv') {
|
||||
snapshot = parseCsv(buffer);
|
||||
} else {
|
||||
return NextResponse.json(
|
||||
{ error: '不支持的文件类型,请上传 .mmap 或 .csv' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
return NextResponse.json(normalizeSnapshot(snapshot));
|
||||
} catch (error) {
|
||||
console.error('导入导图失败', error);
|
||||
return NextResponse.json(
|
||||
{ error: error instanceof Error ? error.message : '导入失败' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -786,15 +786,43 @@ export function MindmapLabClient({
|
||||
const file = event.target.files?.[0];
|
||||
if (!file) return;
|
||||
|
||||
const ext = file.name.split('.').pop()?.toLowerCase();
|
||||
|
||||
try {
|
||||
const text = await file.text();
|
||||
const parsed = JSON.parse(text);
|
||||
const normalized = scrubRichTextSnapshot(normalizeSnapshot(parsed));
|
||||
setSnapshot(normalized);
|
||||
if (initialDocumentId) {
|
||||
scheduleSave(normalized);
|
||||
if (ext === 'json' || ext === 'txt') {
|
||||
const text = await file.text();
|
||||
const parsed = JSON.parse(text);
|
||||
const normalized = scrubRichTextSnapshot(normalizeSnapshot(parsed));
|
||||
setSnapshot(normalized);
|
||||
if (initialDocumentId) {
|
||||
scheduleSave(normalized);
|
||||
}
|
||||
toast.success('导入成功', { description: file.name });
|
||||
} else if (ext === 'mmap' || ext === 'csv') {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
const response = await fetch('/api/mindmap/import', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
});
|
||||
if (!response.ok) {
|
||||
const payload = await response.json().catch(() => null);
|
||||
throw new Error(payload?.error ?? '导入失败');
|
||||
}
|
||||
const payload = (await response.json()) as MindmapSnapshot;
|
||||
const normalized = scrubRichTextSnapshot(normalizeSnapshot(payload));
|
||||
setSnapshot(normalized);
|
||||
if (initialDocumentId) {
|
||||
scheduleSave(normalized);
|
||||
}
|
||||
toast.success('已导入 MindManager/CSV 导图', {
|
||||
description: file.name,
|
||||
});
|
||||
} else {
|
||||
toast.error('暂不支持的文件类型', {
|
||||
description: '请上传 JSON、MMAP 或 CSV 文件',
|
||||
});
|
||||
}
|
||||
toast.success('导入成功', { description: file.name });
|
||||
} catch (error) {
|
||||
console.error('导入导图失败', error);
|
||||
toast.error('导入失败', {
|
||||
@@ -1796,7 +1824,7 @@ export function MindmapLabClient({
|
||||
onClick={handleImportClick}
|
||||
type="button"
|
||||
>
|
||||
导入 JSON
|
||||
导入文件 (JSON/MMAP/CSV)
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2157,7 +2185,7 @@ export function MindmapLabClient({
|
||||
<Input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
accept="application/json"
|
||||
accept=".json,.txt,.mmap,.csv"
|
||||
className="hidden"
|
||||
onChange={handleImportChange}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
const fs=require("fs");
|
||||
const JSZip=require("jszip");
|
||||
const {XMLParser}=require("fast-xml-parser");
|
||||
const arrify=(v)=>Array.isArray(v)?v:(v==null?[]:[v]);
|
||||
const safeText=(v)=>typeof v==='string'&&v.trim()?v.trim():(v&&typeof v==='object'&&v.PlainText?String(v.PlainText).trim():"未命名节点");
|
||||
const pickUid=(obj)=>Object.entries(obj||{}).find(([k,v])=>['OId','Guid','ID','Id','uid'].includes(k)&&typeof v==='string'&&v.trim())?.[1]||`node_${Math.random().toString(16).slice(2)}`;
|
||||
const parseUri=(input)=>{if(typeof input==='string') return input.trim(); if(input && typeof input==='object' && '#text' in input && typeof input['#text']==='string') return input['#text'].trim(); return null;};
|
||||
async function parse(buf){
|
||||
const zip=await JSZip.loadAsync(buf);
|
||||
const xml=await zip.file('Document.xml').async('text');
|
||||
const parser=new XMLParser({ignoreAttributes:false,removeNSPrefix:true,attributeNamePrefix:''});
|
||||
const parsed=parser.parse(xml);
|
||||
const rootTopic=(parsed.Map?.OneTopic?.Topic)||parsed.OneTopic?.Topic||parsed.Topic;
|
||||
const root=Array.isArray(rootTopic)?rootTopic[0]:rootTopic;
|
||||
const uriSet=new Set();
|
||||
(function collect(node){
|
||||
if(!node||typeof node!=='object') return;
|
||||
const images=[...arrify(node.OneImage), ...arrify(node.Image)];
|
||||
images.forEach(img=>{
|
||||
const uri=parseUri(img?.Image?.ImageData?.Uri ?? img?.ImageData?.Uri ?? img?.Uri);
|
||||
if(uri) uriSet.add(uri);
|
||||
});
|
||||
const children=(arrify(node.SubTopics?.Topic)||arrify(node.Topics?.Topic)||arrify(node.Topic));
|
||||
children.forEach(collect);
|
||||
})(root);
|
||||
const uriData=new Map();
|
||||
const detectMime=(b)=> b.slice(0,8).equals(Buffer.from([0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a]))?'image/png':b.slice(0,3).equals(Buffer.from([0xff,0xd8,0xff]))?'image/jpeg':b.slice(0,2).equals(Buffer.from([0x42,0x4d]))?'image/bmp':'application/octet-stream';
|
||||
for(const uri of uriSet){
|
||||
const path=uri.startsWith('mmarch://')?uri.replace('mmarch://',''):uri;
|
||||
const file=zip.file(path);
|
||||
if(!file) continue;
|
||||
const bin=await file.async('nodebuffer');
|
||||
const mime=detectMime(bin);
|
||||
uriData.set(uri,{url:`data:${mime};base64,${bin.toString('base64')}`});
|
||||
}
|
||||
const conv=(node)=>{
|
||||
const data={text:safeText(node?.Text??node?.text)};
|
||||
const images=[...arrify(node?.OneImage), ...arrify(node?.Image)];
|
||||
if(images.length){
|
||||
const first=images[0];
|
||||
const uri=parseUri(first?.Image?.ImageData?.Uri ?? first?.ImageData?.Uri ?? first?.Uri);
|
||||
const size=first?.Image?.ImageSize||first?.ImageSize;
|
||||
if(uri && uriData.has(uri)){
|
||||
data.image=uriData.get(uri).url;
|
||||
if(size?.Width||size?.Height){
|
||||
data.imageSize={width:Number(size.Width)||0,height:Number(size.Height)||0,custom:true};
|
||||
}
|
||||
}
|
||||
}
|
||||
const kids=(arrify(node?.SubTopics?.Topic)||arrify(node?.Topics?.Topic)||arrify(node?.Topic)).map(conv);
|
||||
return {uid:pickUid(node||{}), data, children:kids};
|
||||
};
|
||||
return conv(root);
|
||||
}
|
||||
(async()=>{
|
||||
const root=await parse(fs.readFileSync('..\\design\\导图1.mmap'));
|
||||
const imgs=[];const walk=(n)=>{if(n.data.image) imgs.push(n.data.image.slice(0,40));(n.children||[]).forEach(walk)};walk(root);
|
||||
console.log('image count', imgs.length);
|
||||
console.log('sample', imgs[0]?.slice(0,80));
|
||||
})();
|
||||
Reference in New Issue
Block a user