wendang 0.0.1 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +44 -0
- package/NOTICE.md +7 -0
- package/README.md +169 -2
- package/dist/cjs/document.js +1 -0
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/mindmap.js +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/schema.js +1 -0
- package/dist/cjs/shared/anchor.js +1 -0
- package/dist/cjs/shared/callout.js +1 -0
- package/dist/cjs/shared/code-block.js +1 -0
- package/dist/cjs/shared/code-languages.js +1 -0
- package/dist/cjs/shared/doc-compatibility.js +1 -0
- package/dist/cjs/shared/doc-text.js +1 -0
- package/dist/cjs/shared/document-colors.js +1 -0
- package/dist/cjs/shared/inline-comments.js +1 -0
- package/dist/cjs/shared/mind-map-folding.js +1 -0
- package/dist/cjs/shared/mind-map-phase2.js +1 -0
- package/dist/cjs/shared/mind-map.js +1 -0
- package/dist/cjs/shared/mind-topic-text.js +1 -0
- package/dist/cjs/shared/node-catalog.js +1 -0
- package/dist/cjs/shared/presence.js +1 -0
- package/dist/cjs/shared/safe-href.js +1 -0
- package/dist/cjs/shared/table-cell-format.js +1 -0
- package/dist/cjs/shared/table-layout.js +1 -0
- package/dist/cjs/shared/types.js +1 -0
- package/dist/cjs/shared/video-embed.js +1 -0
- package/dist/cjs/shared/workbook.js +1 -0
- package/dist/cjs/spreadsheet.js +1 -0
- package/dist/cjs/types.js +1 -0
- package/dist/cjs/validation/callout.js +1 -0
- package/dist/cjs/validation/doc-node-whitelist.js +1 -0
- package/dist/cjs/validation/error.js +1 -0
- package/dist/cjs/validation/mind-map.js +1 -0
- package/dist/cjs/validation/schema-version.js +1 -0
- package/dist/cjs/validation/schema.js +1 -0
- package/dist/cjs/validation/video-embed.js +1 -0
- package/dist/cjs/validation/workbook.js +1 -0
- package/dist/esm/document.js +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/mindmap.js +1 -0
- package/dist/esm/schema.js +1 -0
- package/dist/esm/shared/anchor.js +1 -0
- package/dist/esm/shared/callout.js +1 -0
- package/dist/esm/shared/code-block.js +1 -0
- package/dist/esm/shared/code-languages.js +1 -0
- package/dist/esm/shared/doc-compatibility.js +1 -0
- package/dist/esm/shared/doc-text.js +1 -0
- package/dist/esm/shared/document-colors.js +1 -0
- package/dist/esm/shared/inline-comments.js +1 -0
- package/dist/esm/shared/mind-map-folding.js +1 -0
- package/dist/esm/shared/mind-map-phase2.js +1 -0
- package/dist/esm/shared/mind-map.js +1 -0
- package/dist/esm/shared/mind-topic-text.js +1 -0
- package/dist/esm/shared/node-catalog.js +1 -0
- package/dist/esm/shared/presence.js +1 -0
- package/dist/esm/shared/safe-href.js +1 -0
- package/dist/esm/shared/table-cell-format.js +1 -0
- package/dist/esm/shared/table-layout.js +1 -0
- package/dist/esm/shared/types.js +1 -0
- package/dist/esm/shared/video-embed.js +1 -0
- package/dist/esm/shared/workbook.js +1 -0
- package/dist/esm/spreadsheet.js +1 -0
- package/dist/esm/types.js +1 -0
- package/dist/esm/validation/callout.js +1 -0
- package/dist/esm/validation/doc-node-whitelist.js +1 -0
- package/dist/esm/validation/error.js +1 -0
- package/dist/esm/validation/mind-map.js +1 -0
- package/dist/esm/validation/schema-version.js +1 -0
- package/dist/esm/validation/schema.js +1 -0
- package/dist/esm/validation/video-embed.js +1 -0
- package/dist/esm/validation/workbook.js +1 -0
- package/dist/types/document.d.ts +4 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/mindmap.d.ts +4 -0
- package/dist/types/schema.d.ts +4 -0
- package/dist/types/shared/anchor.d.ts +32 -0
- package/dist/types/shared/callout.d.ts +25 -0
- package/dist/types/shared/code-block.d.ts +25 -0
- package/dist/types/shared/code-languages.d.ts +7 -0
- package/dist/types/shared/doc-compatibility.d.ts +1 -0
- package/dist/types/shared/doc-text.d.ts +7 -0
- package/dist/types/shared/document-colors.d.ts +102 -0
- package/dist/types/shared/inline-comments.d.ts +29 -0
- package/dist/types/shared/mind-map-folding.d.ts +3 -0
- package/dist/types/shared/mind-map-phase2.d.ts +19 -0
- package/dist/types/shared/mind-map.d.ts +130 -0
- package/dist/types/shared/mind-topic-text.d.ts +16 -0
- package/dist/types/shared/node-catalog.d.ts +13 -0
- package/dist/types/shared/presence.d.ts +18 -0
- package/dist/types/shared/safe-href.d.ts +2 -0
- package/dist/types/shared/table-cell-format.d.ts +23 -0
- package/dist/types/shared/table-layout.d.ts +4 -0
- package/dist/types/shared/types.d.ts +40 -0
- package/dist/types/shared/video-embed.d.ts +10 -0
- package/dist/types/shared/workbook.d.ts +54 -0
- package/dist/types/spreadsheet.d.ts +1 -0
- package/dist/types/types.d.ts +3 -0
- package/dist/types/validation/callout.d.ts +1 -0
- package/dist/types/validation/doc-node-whitelist.d.ts +2 -0
- package/dist/types/validation/error.d.ts +7 -0
- package/dist/types/validation/mind-map.d.ts +20 -0
- package/dist/types/validation/schema-version.d.ts +1 -0
- package/dist/types/validation/schema.d.ts +15 -0
- package/dist/types/validation/video-embed.d.ts +1 -0
- package/dist/types/validation/workbook.d.ts +1 -0
- package/package.json +88 -6
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createMindMap as t,mindMapDescendants as r,mindMapId as e,mindMapOutline as o,mindMapToJson as n,mindMapTree as i,readMindMap as p,MIND_MAP_VERSION as s}from"./mind-map.js";export const MIND_ICONS=["","💡","🎯","📌","🚩","⭐","✅","⚠️","💬","📅","🔎"];export const MIND_COLORS=["","#18243a","#ffffff","#3864ef","#087f8c","#7950c7","#a66b08","#d94848"];export const MIND_BACKGROUNDS=["","#f3f5fb","#dce7ff","#d3f4e9","#ece0ff","#fff0c2","#ffe1e1","#233044"];export function topicTitle(t){return(t.content??[]).filter(t=>"text"===t.type).map(t=>t.text??"").join("")}export function topicField(t,r){return(t.content?.find(t=>t.type===("description"===r?"mindMapDescription":"mindMapNote"))?.content??[]).map(t=>t.text??"").join("")}export function branchModel(t,e){const o=r(t,e);return{...t,rootId:e,topics:i(t).filter(t=>o.has(t.id)).map(t=>t.id===e?{...t,parentId:""}:t),relations:t.relations?.filter(t=>o.has(t.source)&&o.has(t.target))}}export function ancestorIds(t,r){const e=new Map(i(t).map(t=>[t.id,t])),o=[];let n=e.get(r);for(;n?.parentId&&o.length<500;)o.push(n.parentId),n=e.get(n.parentId);return o}export function searchMindMap(t,r,e=!0){if(!r)return[];const n=r.toLocaleLowerCase(),i=[];for(const r of o(t))for(const t of e?["title","description","note"]:["title"]){const e=("title"===t?r.text:r[t]??"").toLocaleLowerCase();for(let o=0;(o=e.indexOf(n,o))>=0;o+=n.length)if(i.push({id:r.id,field:t,from:o,to:o+n.length}),i.length>5e3)return i}return i}export function parseMindMapImport(r){if((new TextEncoder).encode(r).length>2e6)throw new Error("导入内容过大(最多 2 MB)");if(r.trimStart().startsWith("{")){const t=JSON.parse(r),e="mindMap"===t.type?p(t):"ywiki-mind-map"===t.format&&[1,2,s].includes(t.version)&&Array.isArray(t.topics)?p(n(t)):null;if(!e)throw new Error("导图结构或版本无效");if(e.topics.some(t=>t.id!==e.rootId&&!e.topics.some(r=>r.id===t.parentId)))throw new Error("导入结构包含不存在的父主题");if(e.relations?.some(t=>!e.topics.some(r=>r.id===t.source)||!e.topics.some(r=>r.id===t.target)))throw new Error("导入关系的端点不存在");for(const t of e.topics){const r=new Set;let o=t;for(;o?.parentId;){if(r.has(o.id))throw new Error("导入结构存在循环关系");r.add(o.id),o=e.topics.find(t=>t.id===o.parentId)}}return p(n(e,!0))}const o=r.replace(/\r/g,"").split("\n").filter(t=>t.trim());if(!o.length)throw new Error("请输入缩进列表或导图 JSON");if(o.length>499)throw new Error("导入列表最多 499 行,加中心主题后最多 500 个主题");const i=p(t());i.topics=[i.topics[0]],i.topics[0].text="导入的主题";const c=[{indent:-1,id:i.rootId}];for(let t=0;t<o.length;t++){const r=o[t],n=(r.match(/^\s*/)?.[0]??"").replace(/\t/g," ").length,p=r.trim().replace(/^(?:[-*+]\s+|\d+[.)]\s+)/,"");if(p.length>2e3)throw new Error(`第 ${t+1} 行超过 2000 字符`);for(;c.length>1&&c.at(-1).indent>=n;)c.pop();if(c.length>50)throw new Error(`第 ${t+1} 行嵌套超过 50 层`);const s=e();i.topics.push({id:s,parentId:c.at(-1).id,order:t,text:p}),c.push({indent:n,id:s})}return i}export function mindMapTemplate(r){const o={project:["项目拆解","目标与范围","需求梳理","研发实现","测试验收","上线与复盘"],meeting:["会议讨论","议题","背景信息","讨论结论","待办事项","负责人"],knowledge:["知识目录","基础概念","核心原理","实践案例","常见问题","参考资料"]}[r],n=p(t("both"));return n.topics=o.map((t,r)=>({id:r?e():n.rootId,parentId:r?n.rootId:"",order:r,text:t})),n}export function importBranch(t){return n(t,!0).content??[]}export function parseMindBranch(t){if(!t.startsWith("YWIKI_MINDMAP_BRANCH_V1\n")||t.length>2e6)throw new Error("不是有效的导图分支");const r=JSON.parse(t.slice(t.indexOf("\n")+1));if(!Array.isArray(r)||r.length>1500)throw new Error("分支内容过大");const e=r.filter(t=>"mindMapTopic"===t?.type);if(!e.length||!e[0].attrs)throw new Error("分支缺少主题");const o=r.map(t=>t===e[0]?{...t,attrs:{...t.attrs,parentId:""}}:t),i=p({type:"mindMap",attrs:{id:"clipboard",rootId:e[0].attrs.id,formatVersion:s,layout:"right",theme:"blue",height:380},content:o});if(!i)throw new Error("分支结构不合法");return importBranch(parseMindMapImport(JSON.stringify(n(i))))}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{isSafeHref as t}from"./safe-href.js";import{readableTextColor as e}from"./presence.js";import{mindTopicLineHeight as n,mindTopicTextSize as i,MIND_TOPIC_FONT as o}from"./mind-topic-text.js";export const MIND_MAP_VERSION=3;export const MIND_MAP_MAX_TOPICS=500;export const MIND_MAP_MAX_TEXT=2e3;export const MIND_MAP_LAYOUTS=["right","left","both","down"];export const MIND_MAP_THEMES=["blue","teal","violet","amber"];export const mindMapId=()=>crypto.randomUUID();const validId=t=>"string"==typeof t&&/^[\w-]{1,80}$/.test(t);export function createMindMap(t="right"){const e=mindMapId(),n=mindMapId();return{type:"mindMap",attrs:{id:e,rootId:n,formatVersion:MIND_MAP_VERSION,layout:t,theme:"blue",height:380},content:[{type:"mindMapTopic",attrs:{id:n,parentId:"",order:0},content:[{type:"text",text:"中心主题"}]},...["分支主题 1","分支主题 2","分支主题 3"].map((t,e)=>({type:"mindMapTopic",attrs:{id:mindMapId(),parentId:n,order:e},content:[{type:"text",text:t}]}))]}}export function readMindMap(t){const e=t?.attrs;if("mindMap"!==t?.type||!e||![1,2,MIND_MAP_VERSION].includes(Number(e.formatVersion))||!validId(e.id)||!validId(e.rootId)||!Array.isArray(t.content)||t.content.length<1||t.content.filter(t=>"mindMapTopic"===t?.type).length>500||t.content.length>1500)return null;const n=[],i=new Set;for(const e of t.content.filter(t=>"mindMapRelation"!==t?.type)){if(!e||"object"!=typeof e)return null;const t=e.attrs;if("mindMapTopic"!==e.type||!t||!validId(t.id)||i.has(t.id)||"string"!=typeof t.parentId||""!==t.parentId&&!validId(t.parentId)||"number"!=typeof t.order||!Number.isFinite(t.order)||Math.abs(t.order)>1e12||void 0!==e.content&&!Array.isArray(e.content)||e.content?.some(t=>!t||!["text","mindMapDescription","mindMapNote"].includes(t.type??"")||"text"===t.type&&"string"!=typeof t.text))return null;const o=(e.content??[]).filter(t=>"text"===t.type).map(t=>t.text??"").join("");if(o.length>2e3)return null;i.add(t.id);const r={};if(t.color&&!/^#[0-9a-f]{6}$/i.test(String(t.color)))return null;if(t.background&&!/^#[0-9a-f]{6}$/i.test(String(t.background)))return null;for(const e of["color","background"])if(t[e]&&!["#18243a","#ffffff","#3864ef","#087f8c","#7950c7","#a66b08","#d94848","#f3f5fb","#dce7ff","#d3f4e9","#ece0ff","#fff0c2","#ffe1e1","#233044"].includes(String(t[e])))return null;if(t.shape&&!["round","pill","text"].includes(String(t.shape)))return null;if(t.fontSize&&![12,14,16,18,20].includes(Number(t.fontSize)))return null;if(t.align&&!["left","center","right"].includes(String(t.align)))return null;if(void 0!==t.done&&null!==t.done&&"boolean"!=typeof t.done)return null;if(void 0!==t.side&&null!==t.side&&-1!==t.side&&1!==t.side)return null;if(t.icon&&!["💡","🎯","📌","🚩","⭐","✅","⚠️","💬","📅","🔎"].includes(String(t.icon)))return null;if(t.attachmentId&&("string"!=typeof t.attachmentId||!/^[0-9a-f-]{36}$/i.test(t.attachmentId)))return null;const d=new Set;let a=!1,s=!1;for(const t of e.content??[]){if(void 0!==t.marks&&(!Array.isArray(t.marks)||t.marks.some(t=>!t||!["bold","italic","strike","textStyle","highlight","link"].includes(t.type))))return null;if("text"!==t.type&&(void 0!==t.content&&!Array.isArray(t.content)||t.content?.some(t=>"text"!==t.type||"string"!=typeof t.text)||(t.content??[]).reduce((t,e)=>t+(e.text?.length??0),0)>4e3))return null;if("text"===t.type&&a)return null;if("text"!==t.type){if(d.has(t.type)||"mindMapDescription"===t.type&&s)return null;d.add(t.type),a=!0,"mindMapNote"===t.type&&(s=!0)}}for(const e of["shape","color","background","fontSize","align","icon","done","attachmentId","side"])void 0!==t[e]&&null!==t[e]&&""!==t[e]&&Object.assign(r,{[e]:t[e]});const p=(e.content??[]).filter(t=>"text"===t.type).map(t=>({text:t.text??"",...t.marks?.length?{marks:t.marks}:{}}));p.some(t=>t.marks?.length)&&(r.runs=p);for(const[t,n]of[["mindMapDescription","description"],["mindMapNote","note"]]){const i=e.content?.find(e=>e.type===t);i&&(r[n]=(i.content??[]).map(t=>t.text??"").join(""))}n.push({id:t.id,parentId:t.parentId,order:t.order,text:o,...r})}if(!i.has(e.rootId))return null;const o=t.content.filter(t=>"mindMapRelation"===t.type),r=new Set;for(const t of o){const e=t.attrs;if(!e||!validId(e.id)||i.has(e.id)||r.has(e.id)||!validId(e.source)||!validId(e.target)||e.source===e.target||!["related","depends","reference"].includes(String(e.kind))||"string"!=typeof e.label||e.label.length>200)return null;r.add(e.id)}return{id:e.id,rootId:e.rootId,topics:n,...e.branchStyle?{branchStyle:e.branchStyle}:{},...t.content.some(t=>"mindMapRelation"===t.type)?{relations:t.content.filter(t=>"mindMapRelation"===t.type).map(t=>({...t.attrs}))}:{},layout:MIND_MAP_LAYOUTS.includes(e.layout)?e.layout:"right",theme:MIND_MAP_THEMES.includes(e.theme)?e.theme:"blue",height:"number"==typeof e.height&&Number.isFinite(e.height)?Math.min(800,Math.max(240,e.height)):380}}export function mindMapTree(t){const e=t.topics.map(t=>({...t})),n=new Map(e.map(t=>[t.id,t]));for(const i of e)i.id===t.rootId?i.parentId="":n.has(i.parentId)&&i.parentId!==i.id||(i.parentId=t.rootId);for(const i of[...e].sort((t,e)=>t.id.localeCompare(e.id))){const e=[],o=new Set;let r=i;for(;r&&r.id!==t.rootId;){if(o.has(r.id)){const i=e.slice(e.indexOf(r.id)).sort();n.get(i[0]).parentId=t.rootId;break}o.add(r.id),e.push(r.id),r=n.get(r.parentId)}}return e.sort((t,e)=>t.order-e.order||t.id.localeCompare(e.id))}export function mindMapDescendants(t,e){const n=mindMapTree(t),i=new Set([e]);for(let t=!0;t;){t=!1;for(const e of n)i.has(e.parentId)&&!i.has(e.id)&&(i.add(e.id),t=!0)}return i}export function canMoveMindTopic(t,e,n){return e!==t.rootId&&t.topics.some(t=>t.id===e)&&t.topics.some(t=>t.id===n)&&!mindMapDescendants(t,e).has(n)}export function mindMapBranchSides(t){return new Map(mindMapTree(t).filter(e=>e.parentId===t.rootId).map((e,n)=>[e.id,"left"===t.layout?-1:"both"===t.layout?e.side??(n%2?-1:1):1]))}export function projectMindSideMove(t,e,n,i){if("down"===t.layout||-1!==n&&1!==n||!canMoveMindTopic(t,e,t.rootId))return null;const o=mindMapBranchSides(t),r=mindMapTree(t).filter(i=>i.parentId===t.rootId&&i.id!==e&&o.get(i.id)===n),d=i?r.findIndex(t=>t.id===i):r.length;if(d<0)return null;const a=r.map(t=>t.id);a.splice(d,0,e);const s=new Map(a.map((t,e)=>[t,1024*e]));return{...t,layout:"both",topics:t.topics.map(i=>i.id===e?{...i,parentId:t.rootId,side:n,order:s.get(e)}:i.parentId===t.rootId?{...i,side:o.get(i.id),order:s.get(i.id)??i.order}:i)}}export function textDifference(t,e){let n=0,i=0;for(;n<t.length&&n<e.length&&t[n]===e[n];)n++;for(n>0&&/[\uD800-\uDBFF]/.test(t[n-1])&&n--;i<t.length-n&&i<e.length-n&&t[t.length-i-1]===e[e.length-i-1];)i++;return i>0&&/[\uDC00-\uDFFF]/.test(t[t.length-i])&&i--,{from:n,to:t.length-i,text:e.slice(n,e.length-i)}}export function layoutMindMap(t,e=new Set){const n=mindMapTree(t),o=new Map;for(const t of n){const e=o.get(t.parentId)??[];e.push(t),o.set(t.parentId,e)}const r=n.find(e=>e.id===t.rootId),d=new Map(n.map(t=>{const e=i(t.text,t.fontSize||14,t.id===r.id,t.description||"",!!t.runs?.some(t=>t.marks?.some(t=>"bold"===t.type)),t.attachmentId?180:0);return[t.id,{lines:e.lines,width:e.width,height:Math.max(44,e.titleHeight+22)+(t.description?18*e.descriptionLines.length+6:0)+(t.attachmentId?104:0)+(t.icon||t.done?20:0)+(t.note?20:0)}]})),kids=t=>e.has(t)?[]:o.get(t)??[],a=[],span=e=>Math.max("down"===t.layout?d.get(e.id).width:d.get(e.id).height,kids(e.id).reduce((t,e)=>t+span(e)+20,-20)),place=(e,n,i,o)=>{const r=d.get(e.id);a.push({...e,...r,x:n,y:i,side:o});const s=kids(e.id),p=s.reduce((t,e)=>t+span(e)+20,-20);let c=("down"===t.layout?n+r.width/2:i+r.height/2)-p/2;for(const e of s){const a=d.get(e.id),s=span(e);"down"===t.layout?place(e,c+s/2-a.width/2,i+r.height+60,o):place(e,1===o?n+r.width+64:n-a.width-64,c+s/2-a.height/2,o),c+=s+20}};if("both"===t.layout){const e=d.get(r.id);a.push({...r,...e,x:0,y:0,side:1});const n=kids(r.id),i=mindMapBranchSides(t);for(const t of[-1,1]){const o=n.filter(e=>i.get(e.id)===t);let r=e.height/2-o.reduce((t,e)=>t+span(e)+20,-20)/2;for(const n of o){const i=d.get(n.id),o=span(n);place(n,1===t?e.width+64:-i.width-64,r+o/2-i.height/2,t),r+=o+20}}}else place(r,0,0,"left"===t.layout?-1:1);const s=new Map(a.map(t=>[t.id,t])),p=a.filter(t=>t.id!==r.id).map(e=>{const n=s.get(e.parentId);let i,o,r,d,a;return"down"===t.layout?(i=n.x+n.width/2,o=n.y+n.height,r=e.x+e.width/2,d=e.y,a=`M${i},${o} C${i},${(o+d)/2} ${r},${(o+d)/2} ${r},${d}`):(i=n.x+(1===e.side?n.width:0),o=n.y+n.height/2,r=e.x+(1===e.side?0:e.width),d=e.y+e.height/2,a=`M${i},${o} C${(i+r)/2},${o} ${(i+r)/2},${d} ${r},${d}`),"straight"===t.branchStyle&&(a=`M${i},${o} L${r},${d}`),"step"===t.branchStyle&&(a="down"===t.layout?`M${i},${o} V${(o+d)/2} H${r} V${d}`:`M${i},${o} H${(i+r)/2} V${d} H${r}`),{id:`${n.id}-${e.id}`,source:n.id,target:e.id,path:a}}),c=(t.relations??[]).flatMap(t=>{const e=s.get(t.source),n=s.get(t.target);if(!e||!n)return[];const i=(e.x+e.width/2+n.x+n.width/2)/2,o=7*[...t.label].length/2;return[{left:i-o,right:i+o,top:Math.min(e.y,n.y)-68}]}),l=Math.min(...a.map(t=>t.x),...c.map(t=>t.left))-32,h=Math.min(...a.map(t=>t.y),...c.map(t=>t.top))-32;return{nodes:a,edges:p,bounds:{x:l,y:h,width:Math.max(...a.map(t=>t.x+t.width),...c.map(t=>t.right))-l+32,height:Math.max(...a.map(t=>t.y+t.height))-h+32}}}export const MIND_MAP_PALETTE={blue:"#3864ef",teal:"#087f8c",violet:"#7950c7",amber:"#a66b08"};const escapeXml=t=>t.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"}[t]));export function mindMapSvg(t,i={}){const r=layoutMindMap(t),d=r.bounds,a=MIND_MAP_PALETTE[t.theme];return`<svg xmlns="http://www.w3.org/2000/svg" viewBox="${d.x} ${d.y} ${d.width} ${d.height}" width="${Math.ceil(d.width)}" height="${Math.ceil(d.height)}">`+(i.transparent?"":`<rect x="${d.x}" y="${d.y}" width="${d.width}" height="${d.height}" fill="#fff"/>`)+r.edges.map(t=>`<path d="${t.path}" fill="none" stroke="${a}" stroke-width="2"/>`).join("")+r.nodes.map(r=>{const d=n(r.fontSize||14),s=11+(d-(r.fontSize||14))/2+.8*(r.fontSize||14),p=r.background||(r.id===t.rootId?a:"#f3f5fb"),c=r.color||("text"===r.shape?"#25314a":r.background?e(r.background):r.id===t.rootId?"#fff":"#25314a"),l=r.description?function wrapTopic(t,e=14){const n=[];for(const i of(t||"未命名主题").split("\n")){let t="",o=0;for(const r of i){const i=/[\u0000-\u00ff]/.test(r)?.6:1;o+i>e&&(n.push(t),t="",o=0),t+=r,o+=i}n.push(t||" ")}return n}(r.description):[],h=r.attachmentId&&i.images?.[r.attachmentId];return"<g>"+("text"===r.shape?"":`<rect x="${r.x}" y="${r.y}" width="${r.width}" height="${r.height}" rx="${"pill"===r.shape?24:9}" fill="${escapeXml(p)}" stroke="${a}" stroke-opacity=".3"/>`)+(r.icon||r.done?`<text x="${r.x+r.width/2}" y="${r.y+22}" text-anchor="middle" font-size="16">${r.done?"☑ ":""}${escapeXml(r.icon||"")}</text>`:"")+r.lines.map((t,e)=>function svgHighlights(t,e,n,i,o){if(!t.runs?.some(t=>t.marks?.some(t=>"highlight"===t.type)))return"";const r=t.fontSize||14,width=t=>(/[\u0000-\u00ff]/.test(t)?8:14)*r/14,d=[...e].reduce((t,e)=>t+width(e),0);let a="left"===t.align?i+16:"right"===t.align?i+t.width-d-16:i+(t.width-d)/2,s=n;const p=[];for(const n of e){let e=0;const i=t.runs.find(t=>{const n=e;return e+=t.text.length,s>=n&&s<e}),d=i?.marks?.find(t=>"highlight"===t.type)?.attrs?.color,c=width(n);/^#[0-9a-f]{6}$/i.test(String(d))&&p.push(`<rect x="${a}" y="${o-r}" width="${c}" height="${r+4}" rx="2" fill="${d}"/>`),a+=c,s+=n.length}return p.join("")}(r,t,r.lines.slice(0,e).reduce((t,e)=>t+e.length,0),r.x,r.y+s+e*d+(r.icon||r.done?20:0))).join("")+r.lines.map((t,e)=>`<text x="${r.x+("left"===r.align?16:"right"===r.align?r.width-16:r.width/2)}" y="${r.y+s+e*d+(r.icon||r.done?20:0)}" text-anchor="${"left"===r.align?"start":"right"===r.align?"end":"middle"}" font-family="${escapeXml(o)}" font-size="${r.fontSize||14}" fill="${escapeXml(c)}">${function svgRichLine(t,e,n){if(!t.runs?.length)return escapeXml(e);const i=[];let o=n;for(const n of e){let e=0;const r=t.runs.find(t=>{const n=e;return e+=t.text.length,o>=n&&o<e}),d=r?.marks??[],a=[];for(const t of d)"bold"===t.type&&a.push("font-weight:700"),"italic"===t.type&&a.push("font-style:italic"),"strike"===t.type&&a.push("text-decoration:line-through"),"link"===t.type&&a.push("text-decoration:underline"),"textStyle"===t.type&&/^#[0-9a-f]{6}$/i.test(String(t.attrs?.color))&&a.push(`fill:${t.attrs.color}`);i.push(`<tspan${a.length?` style="${a.join(";")}"`:""}>${escapeXml(n)}</tspan>`),o+=n.length}return i.join("")}(r,t,r.lines.slice(0,e).reduce((t,e)=>t+e.length,0))}</text>`).join("")+l.map((t,e)=>`<text x="${r.x+r.width/2}" y="${r.y+28+r.lines.length*d+18*e+(r.icon||r.done?20:0)}" text-anchor="middle" font-size="12" fill="#677184">${escapeXml(t)}</text>`).join("")+(r.note?`<text x="${r.x+r.width/2}" y="${r.y+r.height-(r.attachmentId?112:10)}" text-anchor="middle" font-size="10" fill="#677184">▤ 备注</text>`:"")+(h&&/^data:image\/(png|jpeg|webp|gif);base64,/.test(h)?`<image x="${r.x+10}" y="${r.y+r.height-105}" width="${r.width-20}" height="95" href="${h}" preserveAspectRatio="xMidYMid meet"/>`:"")+"</g>"}).join("")+mindRelationPaths(t,r).map(t=>`<path d="${t.path}" fill="none" stroke="#8b5fc7" stroke-width="2" stroke-dasharray="6 4"/><text x="${t.x}" y="${t.y}" font-size="12" fill="#7950c7">${escapeXml(t.label||{related:"相关",depends:"依赖",reference:"参考"}[t.kind])}</text>`).join("")+"</svg>"}export function mindMapOutline(t){const e=mindMapTree(t),n=[],visit=(t,i)=>{const o=e.find(e=>e.id===t);if(o){n.push({...o,depth:i});for(const n of e.filter(e=>e.parentId===t))visit(n.id,i+1)}};return visit(t.rootId,0),n}export function mindMapMarkdown(e){const escape=t=>t.replace(/([\\`*_[\]<>])/g,"\\$1").replace(/\n/g," ");return mindMapOutline(e).map(e=>{const n=(e.runs??[{text:e.text}]).map(e=>{let n=escape(e.text);for(const i of e.marks??[])"bold"===i.type&&(n=`**${n}**`),"italic"===i.type&&(n=`*${n}*`),"strike"===i.type&&(n=`~~${n}~~`),"link"===i.type&&t(i.attrs?.href)&&(n=`[${n}](${String(i.attrs.href).replace(/[()]/g,t=>encodeURIComponent(t))})`);return n}).join("")||"未命名主题";return`${" ".repeat(e.depth)}- ${e.done?"[x] ":""}${e.icon?e.icon+" ":""}${n}${e.description?" — "+escape(e.description):""}${e.note?"(备注:"+escape(e.note)+")":""}${e.attachmentId?" [图片附件:"+e.attachmentId+"]":""}`}).join("\n")+((e.relations?.length??0)>0?"\n\n关系:\n"+e.relations.map(t=>`- ${escape(e.topics.find(e=>e.id===t.source)?.text||"已移除主题")} → ${escape(e.topics.find(e=>e.id===t.target)?.text||"已移除主题")}:${escape(t.label||t.kind)}`).join("\n"):"")}export function mindRelationPaths(t,e){const n=new Map(e.nodes.map(t=>[t.id,t]));return(t.relations??[]).flatMap(t=>{const e=n.get(t.source),i=n.get(t.target);if(!e||!i||e===i)return[];const o=e.x+e.width/2,r=e.y,d=i.x+i.width/2,a=i.y,s=Math.min(r,a)-48;return[{...t,path:`M${o},${r} C${o},${s} ${d},${s} ${d},${a}`,x:(o+d)/2,y:s+8}]})}export function mindMapToJson(t,e=!1){const n=new Map(t.topics.map(t=>[t.id,e?mindMapId():t.id]));return{type:"mindMap",attrs:{id:e?mindMapId():t.id,rootId:n.get(t.rootId),formatVersion:MIND_MAP_VERSION,layout:t.layout,theme:t.theme,height:t.height,branchStyle:t.branchStyle||"curve"},content:[...t.topics.map(t=>{const{text:e,runs:i,description:o,note:r,...d}=t;return{type:"mindMapTopic",attrs:{...d,id:n.get(t.id),parentId:n.get(t.parentId)??""},content:[...(i??(e?[{text:e}]:[])).map(t=>({type:"text",...t})),...o?[{type:"mindMapDescription",content:[{type:"text",text:o}]}]:[],...r?[{type:"mindMapNote",content:[{type:"text",text:r}]}]:[]]}}),...(t.relations??[]).map(t=>({type:"mindMapRelation",attrs:{...t,id:e?mindMapId():t.id,source:n.get(t.source),target:n.get(t.target)}}))]}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const MIND_TOPIC_LINE_HEIGHT=22;export const mindTopicLineHeight=(e=14)=>Math.max(22,Math.ceil(1.4*e));export const MIND_TOPIC_PADDING=34;export const MIND_TOPIC_FONT='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif';const e=new Map;let t;export function setMindTextMeasureFactory(n){t=n,e.clear()}export function mindTextMeasure(e=14,n=400){const i=t?.(e,n);return i||(t=>[...t].reduce((t,n)=>t+(/[\u0000-\u00ff]/.test(n)?9:14)*e/14,0))}export function wrapMindText(e,t,n){const i=[],o="function"==typeof Intl.Segmenter?new Intl.Segmenter(void 0,{granularity:"grapheme"}):null;for(const s of e.split("\n")){let e="";const r=o?[...o.segment(s)].map(e=>e.segment):[...s];for(const o of r)e&&n(e+o)>t&&(i.push(e),e=""),e+=o;i.push(e)}return i}export function mindTopicTextSize(t,n=14,i=!1,o="",s=!1,r=0){const a=JSON.stringify([t,n,i,o,s,r]),c=e.get(a);if(c)return c;const p=mindTextMeasure(n,s?700:i?600:400),u=mindTextMeasure(12),l=t||"未命名主题",m=Math.max(...l.split("\n").map(p),...o.split("\n").map(u)),x=Math.min(240,Math.max(i?128:100,r,Math.ceil(m+34+2))),M=wrapMindText(l,x-34,p),f={width:x,lines:M,descriptionLines:o?wrapMindText(o,x-34,u):[],titleHeight:M.length*mindTopicLineHeight(n)};return e.size>=512&&e.delete(e.keys().next().value),e.set(a,f),f}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const NODE_CATALOG=[{type:"doc",kind:"block",label:"文档",topLevel:!0},{type:"paragraph",kind:"block",label:"段落",topLevel:!0},{type:"heading",kind:"block",label:"标题",topLevel:!0},{type:"bulletList",kind:"block",label:"无序列表",topLevel:!0},{type:"orderedList",kind:"block",label:"有序列表",topLevel:!0},{type:"taskList",kind:"block",label:"待办列表",topLevel:!0},{type:"listItem",kind:"block",label:"列表项"},{type:"taskItem",kind:"block",label:"待办项"},{type:"blockquote",kind:"block",label:"引用块",topLevel:!0},{type:"codeBlock",kind:"block",label:"代码块",topLevel:!0},{type:"horizontalRule",kind:"atom",atom:!0,label:"分割线",topLevel:!0},{type:"table",kind:"block",label:"表格",topLevel:!0},{type:"tableRow",kind:"block",label:"表格行"},{type:"tableHeader",kind:"block",label:"表头单元格"},{type:"tableCell",kind:"block",label:"单元格"},{type:"image",kind:"atom",atom:!0,label:"图片",topLevel:!0},{type:"fileAttachment",kind:"atom",atom:!0,label:"视频或文件",topLevel:!0},{type:"videoEmbed",kind:"atom",atom:!0,label:"嵌入视频",topLevel:!0},{type:"mindMap",kind:"block",label:"思维导图",topLevel:!0},{type:"mindMapDescription",kind:"inline",label:"主题描述"},{type:"mindMapNote",kind:"inline",label:"主题备注"},{type:"mindMapRelation",kind:"block",label:"主题关系"},{type:"mindMapTopic",kind:"block",label:"导图主题"},{type:"callout",kind:"block",label:"高亮块",topLevel:!0},{type:"columns",kind:"block",label:"分栏",topLevel:!0},{type:"column",kind:"block",label:"栏"},{type:"pageLink",kind:"atom",atom:!0,label:"页面引用"},{type:"mention",kind:"atom",atom:!0,label:"提及用户"},{type:"mathInline",kind:"atom",atom:!0,label:"行内公式"},{type:"mathBlock",kind:"atom",atom:!0,label:"公式块",topLevel:!0},{type:"text",kind:"text",label:"文本"},{type:"hardBreak",kind:"inline",atom:!0,label:"换行"}];const e=new Map(NODE_CATALOG.map(e=>[e.type,e]));export function nodeMeta(l){return l?e.get(l):void 0}export const UNKNOWN_NODE_LABEL="此内容由更高版本的编辑器创建,当前版本无法显示";export function headingTag(e){const l="number"==typeof e?e:1;return`h${Math.min(Math.max(l,1),6)}`}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const PRESENCE_PALETTE=["#2f6feb","#127a45","#b45309","#c026d3","#dc2626","#0e7490","#7c3aed","#9a4a08"];export function colorForUser(e){return PRESENCE_PALETTE[hashIndex(e,PRESENCE_PALETTE.length)]}export function hashIndex(e,t){let r=2166136261;for(let t=0;t<e.length;t++)r^=e.charCodeAt(t),r=r+((r<<1)+(r<<4)+(r<<7)+(r<<8)+(r<<24))>>>0;return t>0?r%t:0}export function initialOf(e){const t=e.trim();return""===t?"?":t.slice(0,1).toUpperCase()}export function collectPresence(e,t){const r=new Map;for(const n of e)n.userId&&(t&&n.userId===t||r.has(n.userId)||r.set(n.userId,{userId:n.userId,displayName:n.displayName||"匿名",color:colorForUser(n.userId)}));return[...r.values()].sort((e,t)=>e.displayName.localeCompare(t.displayName,"zh-Hans-CN"))}export function relativeLuminance(e){const t=parseHex(e);if(!t)return 0;const[r,n,o]=t.map(e=>{const t=e/255;return t<=.03928?t/12.92:((t+.055)/1.055)**2.4});return.2126*r+.7152*n+.0722*o}export const DARK_TEXT="#1f2937";export function contrastRatio(e,t){const r=relativeLuminance(e),n=relativeLuminance(t);return(Math.max(r,n)+.05)/(Math.min(r,n)+.05)}export function readableTextColor(e){return contrastRatio(e,"#ffffff")>=contrastRatio(e,"#1f2937")?"#ffffff":"#1f2937"}export function parseHex(e){const t=/^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(e.trim());if(!t)return null;let r=t[1];return 3===r.length&&(r=r.split("").map(e=>e+e).join("")),[parseInt(r.slice(0,2),16),parseInt(r.slice(2,4),16),parseInt(r.slice(4,6),16)]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const t=/^(https?:|mailto:)/i;export function isSafeHref(e){if("string"!=typeof e)return!1;const r=e.replace(/[\u0000-\u001f\u007f\s]/g,"").toLowerCase();return!(""===r||!r.startsWith("/")&&!r.startsWith("#")&&!t.test(r)&&r.includes(":"))}export function safeHref(t){return isSafeHref(t)?String(t):"#"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const CELL_BACKGROUNDS=[{name:"默认",value:null},{name:"灰",value:"#f1f3f5"},{name:"黄",value:"#fff3bf"},{name:"绿",value:"#d3f9d8"},{name:"蓝",value:"#d0ebff"},{name:"紫",value:"#e5dbff"},{name:"红",value:"#ffe3e3"}];export function cellBackground(e){return"string"==typeof e&&CELL_BACKGROUNDS.some(a=>a.value===e)?e:null}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const TABLE_CELL_MIN_WIDTH=120;export function tableColumnWidths(t){const e=t.content?.find(t=>"tableRow"===t.type),n=[];for(const t of e?.content??[]){const e=Number(t.attrs?.colspan??1),o=Number.isSafeInteger(e)&&e>0?Math.min(e,1e3):1,r=t.attrs?.colwidth;for(let t=0;t<o;t+=1){const e=Array.isArray(r)?r[t]:void 0;n.push("number"==typeof e&&Number.isFinite(e)&&e>0?e:0)}}return n}export function tableMinWidth(t){return t.length>0?t.reduce((t,e)=>t+(e>0?e:120),0):void 0}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function normalizeVideoEmbed(e){if(!e||"object"!=typeof e)return null;const t=e,r="youtube"===t.provider?"string"==typeof t.videoId&&/^[\w-]{11}$/.test(t.videoId):"bilibili"===t.provider&&"string"==typeof t.videoId&&/^(BV[A-Za-z0-9]{10}|av[1-9]\d{0,15})$/.test(t.videoId),i=t.page??1,o=t.startSeconds??0;return!r||!Number.isInteger(i)||Number(i)<1||Number(i)>1e3||!Number.isInteger(o)||Number(o)<0||Number(o)>604800?null:{provider:t.provider,videoId:t.videoId,page:"youtube"===t.provider?1:Number(i),startSeconds:Number(o)}}function seconds(e){if(!e)return 0;if(/^\d+$/.test(e))return Number(e);const t=/^(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$/.exec(e);return t&&t[0]?3600*Number(t[1]??0)+60*Number(t[2]??0)+Number(t[3]??0):NaN}export function parseVideoUrl(e){let t;try{t=new URL(e.trim())}catch{return null}if(!["https:","http:"].includes(t.protocol)||t.username||t.password||t.port)return null;const r=t.hostname.toLowerCase(),i=t.pathname.split("/").filter(Boolean);let o,s;if("youtu.be"===r)o="youtube",s=1===i.length?i[0]:null;else if(["youtube.com","www.youtube.com","m.youtube.com","www.youtube-nocookie.com"].includes(r))o="youtube",s="/watch"===t.pathname?t.searchParams.get("v"):["embed","shorts","live"].includes(i[0])&&2===i.length?i[1]:null;else if(["bilibili.com","www.bilibili.com","m.bilibili.com"].includes(r))o="bilibili",s="video"===i[0]&&2===i.length?i[1]:null;else{if("player.bilibili.com"!==r||"/player.html"!==t.pathname)return null;o="bilibili",s=t.searchParams.get("bvid")??(t.searchParams.get("aid")?`av${t.searchParams.get("aid")}`:null)}const a=/^#t=(.*)$/.exec(t.hash)?.[1]??null;return normalizeVideoEmbed({provider:o,videoId:s,page:Number(t.searchParams.get("p")??1),startSeconds:seconds(t.searchParams.get("start")??t.searchParams.get("t")??a)})}export function videoSourceUrl(e){const t=normalizeVideoEmbed(e);if(!t)return"";const r=new URL("youtube"===t.provider?`https://www.youtube.com/watch?v=${t.videoId}`:`https://www.bilibili.com/video/${t.videoId}`);return"bilibili"===t.provider&&t.page>1&&r.searchParams.set("p",String(t.page)),t.startSeconds&&r.searchParams.set("t",String(t.startSeconds)),r.href}export function videoPlayerUrl(e){const t=normalizeVideoEmbed(e);if(!t)return"";const r=new URL("youtube"===t.provider?`https://www.youtube.com/embed/${t.videoId}`:"https://player.bilibili.com/player.html");return r.searchParams.set("autoplay","0"),"youtube"===t.provider?(r.searchParams.set("playsinline","1"),t.startSeconds&&r.searchParams.set("start",String(t.startSeconds))):(r.searchParams.set(t.videoId.startsWith("BV")?"bvid":"aid",t.videoId.startsWith("BV")?t.videoId:t.videoId.slice(2)),r.searchParams.set("p",String(t.page)),r.searchParams.set("t",String(t.startSeconds)),r.searchParams.set("danmaku","0")),r.href}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const WORKBOOK_LIMITS={sheets:20,rows:1e4,columns:200,cells:5e4,bytes:4194304};export function isWorkbookDocument(e){return!!e&&"object"==typeof e&&"workbook"===e.type}export function emptyWorkbook(e){return{type:"workbook",formatVersion:1,workbook:{id:e,name:"工作簿",appVersion:"1.1.0",locale:"zhCN",sheetOrder:["sheet1"],sheets:{sheet1:{id:"sheet1",name:"Sheet1",rowCount:1e3,columnCount:26,cellData:{}}}}}}function record(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}function index(e,o){return/^(0|[1-9]\d*)$/.test(e)&&Number(e)<o}export function validateWorkbook(e){const fail=e=>{throw new Error(`工作表格式无效:${e}`)};isWorkbookDocument(e)&&1===e.formatVersion&&record(e.workbook)||fail("不支持的版本");const o=e,t=JSON.stringify(o);(new TextEncoder).encode(t).byteLength>WORKBOOK_LIMITS.bytes&&fail("超过 4MB");let r=0;const walk=(e,o)=>{if((++r>5e5||o>40)&&fail("结构过大或嵌套过深"),"number"!=typeof e||Number.isFinite(e)||fail("包含非法数值"),Array.isArray(e))e.forEach(e=>walk(e,o+1));else if(record(e)){Object.getPrototypeOf(e)!==Object.prototype&&null!==Object.getPrototypeOf(e)&&fail("包含非法对象");for(const[t,r]of Object.entries(e))["__proto__","constructor","prototype"].includes(t)&&fail("包含非法属性"),walk(r,o+1)}};walk(o,0);const n=o.workbook;("string"!=typeof n.id||!n.id||n.id.length>128||"string"!=typeof n.name||n.name.length>256)&&fail("工作簿名称或标识无效"),Array.isArray(n.sheetOrder)&&n.sheetOrder.length&&!(n.sheetOrder.length>WORKBOOK_LIMITS.sheets)&&new Set(n.sheetOrder).size===n.sheetOrder.length&&record(n.sheets)&&Object.keys(n.sheets).length===n.sheetOrder.length||fail("表页数量或顺序无效");let s=0;const a=new Set;for(const e of n.sheetOrder){("string"!=typeof e||!e||e.length>128)&&fail("表页标识无效");const o=n.sheets[e];if((!record(o)||o.id!==e||"string"!=typeof o.name||!o.name.trim()||o.name.length>31||/[\\/*?:[\]]/.test(o.name)||a.has(o.name.toLowerCase()))&&fail("表页名称无效或重复(最多 31 字符)"),a.add(o.name.toLowerCase()),(!Number.isInteger(o.rowCount)||o.rowCount<1||o.rowCount>WORKBOOK_LIMITS.rows||!Number.isInteger(o.columnCount)||o.columnCount<1||o.columnCount>WORKBOOK_LIMITS.columns)&&fail("行列超出上限"),void 0!==o.cellData){record(o.cellData)||fail("单元格结构无效");for(const[e,t]of Object.entries(o.cellData)){index(e,o.rowCount)&&record(t)||fail("单元格行号无效");for(const[e,r]of Object.entries(t))++s>WORKBOOK_LIMITS.cells&&fail("单元格超过 50000 个"),index(e,o.columnCount)||fail("单元格列号无效"),null!==r&&(record(r)||fail("单元格结构无效"),void 0===r.v||null===r.v||["string","number","boolean"].includes(typeof r.v)||fail("单元格值无效"),"string"==typeof r.v&&r.v.length>16e3&&fail("单元格文字过长"),void 0!==r.f&&("string"!=typeof r.f||r.f.length>4e3)&&fail("公式无效"),void 0!==r.t&&(!Number.isInteger(r.t)||Number(r.t)<0||Number(r.t)>4)&&fail("单元格类型无效"),void 0===r.s||null===r.s||"string"==typeof r.s||record(r.s)||fail("单元格样式无效"),void 0===r.p||record(r.p)&&(void 0===r.p.body||record(r.p.body)&&"string"==typeof r.p.body.dataStream&&!(r.p.body.dataStream.length>16e3))||fail("单元格富文本无效"))}}for(const[e,t]of[[o.rowData,o.rowCount],[o.columnData,o.columnCount]])void 0===e||record(e)&&!Object.keys(e).some(e=>!index(e,t))||fail("行列属性越界");if(void 0!==o.mergeData){(!Array.isArray(o.mergeData)||o.mergeData.length>WORKBOOK_LIMITS.cells)&&fail("合并区域无效");const e=new Set;for(const t of o.mergeData){(!record(t)||![t.startRow,t.endRow,t.startColumn,t.endColumn].every(Number.isInteger)||t.startRow<0||t.endRow<t.startRow||t.endRow>=o.rowCount||t.startColumn<0||t.endColumn<t.startColumn||t.endColumn>=o.columnCount)&&fail("合并区域越界");const r=(t.endRow-t.startRow+1)*(t.endColumn-t.startColumn+1);e.size+r>2e5&&fail("合并区域总范围过大");for(let o=t.startRow;o<=t.endRow;o++)for(let r=t.startColumn;r<=t.endColumn;r++){const t=o*WORKBOOK_LIMITS.columns+r;e.has(t)&&fail("合并区域重叠"),e.add(t)}}}}return JSON.parse(t)}export function workbookCellText(e){return e?e.p?.body?.dataStream?e.p.body.dataStream.replace(/[\r\n]+$/,""):void 0!==e.v&&null!==e.v?String(e.v):e.f??"":""}export function workbookText(e){return e.workbook.sheetOrder.map(o=>{const t=e.workbook.sheets[o];return`${t.name} ${Object.values(t.cellData??{}).flatMap(e=>Object.values(e).map(workbookCellText)).join(" ")}`}).join(" ").slice(0,2e5)}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export*from"./shared/workbook.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export*from"./shared/types.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export*from"../shared/callout.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const ALLOWED_NODES=new Set(["mindMapDescription","mindMapNote","mindMapRelation","mindMap","mindMapTopic","doc","paragraph","text","heading","bulletList","orderedList","listItem","taskList","taskItem","blockquote","codeBlock","horizontalRule","hardBreak","image","fileAttachment","videoEmbed","callout","columns","column","table","tableRow","tableHeader","tableCell","pageLink","mention","mathInline","mathBlock"]);export const ALLOWED_MARKS=new Set(["bold","italic","strike","code","link","textStyle","highlight"]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export class ContentValidationError extends Error{code;status;details;constructor(t,a,i=422,r){super(a),this.code=t,this.status=i,this.details=r,this.name="ContentValidationError"}static validation(t){return new ContentValidationError("validation.failed",t)}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const idOk=t=>"string"==typeof t&&/^[\w-]{1,80}$/.test(t);export function validateMindMap(t){const e=t.attrs;if(!e||![1,2,3].includes(Number(e.formatVersion))||!idOk(e.id)||!idOk(e.rootId)||!["right","left","both","down"].includes(String(e.layout))||!["blue","teal","violet","amber"].includes(String(e.theme))||"number"!=typeof e.height||!Number.isFinite(e.height)||e.height<240||e.height>800)throw new Error("思维导图属性或格式版本无效");if(!Array.isArray(t.content)||t.content.length<1||t.content.filter(t=>"mindMapTopic"===t?.type).length>500||t.content.length>1500)throw new Error("思维导图需要 1~500 个主题");const r=new Set;for(const e of t.content.filter(t=>"mindMapRelation"!==t?.type)){const t=e?.attrs;if(!e||"mindMapTopic"!==e.type||!t||!idOk(t.id)||r.has(t.id)||"string"!=typeof t.parentId||""!==t.parentId&&!idOk(t.parentId)||"number"!=typeof t.order||!Number.isFinite(t.order)||Math.abs(t.order)>1e12||void 0!==e.content&&!Array.isArray(e.content))throw new Error("思维导图主题结构无效");r.add(t.id);for(const[e,r]of Object.entries({shape:["round","pill","text"],align:["left","center","right"]}))if(void 0!==t[e]&&null!==t[e]&&!r.includes(String(t[e])))throw new Error("导图主题样式无效");for(const e of["color","background"])if(t[e]&&!["#18243a","#ffffff","#3864ef","#087f8c","#7950c7","#a66b08","#d94848","#f3f5fb","#dce7ff","#d3f4e9","#ece0ff","#fff0c2","#ffe1e1","#233044"].includes(String(t[e])))throw new Error("导图颜色无效");if(void 0!==t.fontSize&&null!==t.fontSize&&![12,14,16,18,20].includes(Number(t.fontSize)))throw new Error("导图字号无效");if(t.icon&&!["💡","🎯","📌","🚩","⭐","✅","⚠️","💬","📅","🔎"].includes(String(t.icon)))throw new Error("导图图标无效");if(void 0!==t.done&&null!==t.done&&"boolean"!=typeof t.done)throw new Error("导图状态无效");if(void 0!==t.side&&null!==t.side&&-1!==t.side&&1!==t.side)throw new Error("导图分支方向无效");if(t.attachmentId&&("string"!=typeof t.attachmentId||!/^[0-9a-f-]{36}$/i.test(t.attachmentId)))throw new Error("导图图片引用无效");let n=0;const o=new Set;let i=!1,d=!1;for(const t of e.content??[])if(t&&["mindMapDescription","mindMapNote"].includes(t.type??"")){if("mindMapDescription"===t.type&&d)throw new Error("主题描述必须位于备注之前");if(i=!0,"mindMapNote"===t.type&&(d=!0),o.has(t.type)||void 0!==t.content&&!Array.isArray(t.content)||t.content?.some(t=>"text"!==t?.type||"string"!=typeof t.text||void 0!==t.content)||(t.content??[]).reduce((t,e)=>t+(e.text?.length??0),0)>4e3)throw new Error("导图描述或备注无效");o.add(t.type)}else{if(!t||"text"!==t.type||"string"!=typeof t.text||void 0!==t.content)throw new Error("思维导图主题只支持纯文本");if(i)throw new Error("主题标题必须位于描述和备注之前");n+=t.text.length}if(n>2e3)throw new Error("单个主题文字不得超过 2000 字符")}if(!r.has(e.rootId))throw new Error("思维导图缺少中心主题");if(e.branchStyle&&!["curve","straight","step"].includes(String(e.branchStyle)))throw new Error("分支样式无效");const n=new Set;for(const e of t.content.filter(t=>"mindMapRelation"===t?.type)){const t=e.attrs;if(!t||!idOk(t.id)||r.has(t.id)||n.has(t.id)||!idOk(t.source)||!idOk(t.target)||t.source===t.target||!["related","depends","reference"].includes(String(t.kind))||"string"!=typeof t.label||t.label.length>200||e.content?.length)throw new Error("导图关系线无效");n.add(t.id)}}export function mindMapOutline(t){const e=String(t.attrs?.rootId??"");if(!idOk(e)||!Array.isArray(t.content)||t.content.filter(t=>"mindMapTopic"===t?.type).length>500||t.content.length>1500)return[];const r=(t.content??[]).filter(t=>"mindMapTopic"===t.type).map(t=>({id:String(t.attrs?.id??""),parentId:String(t.attrs?.parentId??""),order:Number(t.attrs?.order)||0,text:(t.content??[]).filter(t=>"text"===t.type).map(t=>t.text??"").join(""),...Object.fromEntries((t.content??[]).filter(t=>"mindMapDescription"===t.type||"mindMapNote"===t.type).map(t=>["mindMapDescription"===t.type?"description":"note",(t.content??[]).map(t=>t.text??"").join("")])),...t.attrs?.attachmentId?{attachmentId:String(t.attrs.attachmentId)}:{}}));if(!r.some(t=>t.id===e))return[];const n=new Map(r.map(t=>[t.id,t]));for(const t of r)t.id===e?t.parentId="":n.has(t.parentId)&&t.parentId!==t.id||(t.parentId=e);for(const t of[...r].sort((t,e)=>t.id.localeCompare(e.id))){const r=[],o=new Set;let i=t;for(;i&&i.id!==e;){if(o.has(i.id)){n.get(r.slice(r.indexOf(i.id)).sort()[0]).parentId=e;break}r.push(i.id),o.add(i.id),i=n.get(i.parentId)}}r.sort((t,e)=>t.order-e.order||t.id.localeCompare(e.id));const o=[],i=new Set,visit=(t,e)=>{const d=n.get(t);if(d&&!i.has(t)){i.add(t),o.push({...d,depth:e});for(const n of r.filter(e=>e.parentId===t))visit(n.id,e+1)}};return visit(e,0),o}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const CONTENT_SCHEMA_VERSION=10;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ContentValidationError as t}from"./error.js";const e="validation.failed";import{ALLOWED_MARKS as n,ALLOWED_NODES as r}from"./doc-node-whitelist.js";import{normalizeVideoEmbed as o}from"./video-embed.js";import{CONTENT_SCHEMA_VERSION as i}from"./schema-version.js";import{validateMindMap as a}from"./mind-map.js";import{validCalloutAttrs as s}from"./callout.js";import{isWorkbookDocument as l,validateWorkbook as c,workbookText as f}from"./workbook.js";export{r as ALLOWED_NODES};export const SCHEMA_VERSION=i;const d={heading:new Set(["level","textAlign","indent"]),paragraph:new Set(["textAlign","indent"]),orderedList:new Set(["start"]),textStyle:new Set(["color"]),highlight:new Set(["color"]),image:new Set(["attachmentId","alt","width","align"]),fileAttachment:new Set(["attachmentId","filename","mimeType","size"]),videoEmbed:new Set(["provider","videoId","page","startSeconds"]),mindMap:new Set(["id","rootId","formatVersion","layout","theme","height","branchStyle"]),mindMapTopic:new Set(["id","parentId","order","shape","color","background","fontSize","align","icon","done","attachmentId","side"]),mindMapRelation:new Set(["id","source","target","kind","label"]),callout:new Set(["tone","icon","textColor","borderColor","backgroundColor"]),codeBlock:new Set(["language"]),link:new Set(["href","target","rel"]),pageLink:new Set(["pageId","label"]),mention:new Set(["userId","label"]),tableCell:new Set(["colspan","rowspan","align","colwidth","backgroundColor"]),tableHeader:new Set(["colspan","rowspan","align","colwidth","backgroundColor"]),taskItem:new Set(["checked"]),mathInline:new Set(["latex"]),mathBlock:new Set(["latex"])},p=new Set(["colspan","rowspan"]),h=new Set(["#f1f3f5","#fff3bf","#d3f9d8","#d0ebff","#e5dbff","#ffe3e3"]);function isTableCellType(t){return"tableCell"===t||"tableHeader"===t}function coercePositiveInt(t){const e="number"==typeof t?t:Number.parseInt(String(t??""),10);if(!Number.isFinite(e))return;const n=Math.floor(e);return n>=1?n:void 0}function coerceColwidth(t){if(null===t)return null;let e;if(Array.isArray(t))e=t;else{if("string"!=typeof t)return;{const n=t.trim();if(!n)return null;if(n.startsWith("["))try{const t=JSON.parse(n);e=Array.isArray(t)?t:[]}catch{return}else e=n.split(",")}}if(0===e.length)return null;const n=e.map(t=>{const e="number"==typeof t?t:Number.parseInt(String(t??""),10);return Number.isFinite(e)&&e>0?Math.floor(e):0});return n.some(t=>t>0)?n:null}export class ContentSchemaService{validate(i,f){if(!Number.isSafeInteger(f)||f<1)throw t.validation("内容版本必须是正整数");if(l(i)){if(10!==f)throw t.validation("工作表需要当前编辑器版本");try{return{doc:c(i),schemaVersion:f,linkTargetIds:[],attachmentIds:[],mentionUserIds:[]}}catch(e){throw t.validation(e instanceof Error?e.message:"工作表格式无效")}}if("object"!=typeof i||null===i||"doc"!==i.type)throw t.validation("正文格式非法:根节点必须是 doc");if(f>SCHEMA_VERSION)throw new t(e,`正文版本 ${f} 高于服务端支持的 ${SCHEMA_VERSION},请升级后再编辑`,422);const m=new Set,g=new Set,u=new Set;let w=0;const walk=(i,l,c="")=>{if("object"!=typeof i||null===i)throw t.validation("正文包含非法节点");if(l>100)throw t.validation("正文嵌套层级过深");const f=i,y=f.type;if(("mindMapTopic"===y||"mindMapRelation"===y)&&"mindMap"!==c)throw t.validation("导图主题只能放在思维导图内");if(["mindMapDescription","mindMapNote"].includes(String(y))&&"mindMapTopic"!==c)throw t.validation("导图描述只能放在主题内");if("mindMap"===y)try{a(f)}catch(e){throw t.validation(e instanceof Error?e.message:"思维导图结构无效")}if("callout"===y&&!s(f.attrs??{}))throw t.validation("高亮块属性无效");if("string"==typeof y){if(!r.has(y))throw new t(e,`正文包含服务端不支持的节点「${y}」,已拒绝保存以避免内容丢失`,422,{nodeType:y,schemaVersion:SCHEMA_VERSION});if(w+=1,w>2e4)throw t.validation("正文节点数超出上限")}const S={};if("string"==typeof y&&(S.type=y),f.attrs&&"object"==typeof f.attrs){const t="string"==typeof y?d[y]:void 0,e={};for(const[n,r]of Object.entries(f.attrs))if(t&&t.has(n))if(isTableCellType(y)&&"backgroundColor"===n)"string"==typeof r&&h.has(r)&&(e[n]=r);else if("href"!==n||"string"!=typeof r||this.isSafeHref(r)){if(isTableCellType(y)&&p.has(n)){const t=coercePositiveInt(r);if(void 0===t)continue;e[n]=t;continue}if(isTableCellType(y)&&"colwidth"===n){const t=coerceColwidth(r);if(void 0===t)continue;e[n]=t;continue}e[n]=r}Object.keys(e).length>0&&(S.attrs=e)}if("videoEmbed"===y){const e=o(f.attrs);if(!e)throw t.validation("视频属性无效,请重新插入视频");S.attrs={...e}}if("string"==typeof f.text&&(S.text=f.text),Array.isArray(f.marks)){const t=f.marks.filter(t=>"string"==typeof t.type&&n.has(t.type)).map(t=>{const e={type:t.type};if(t.attrs&&"object"==typeof t.attrs){const n=d[t.type],r={};for(const[e,o]of Object.entries(t.attrs))n?.has(e)&&("href"!==e||"string"==typeof o&&this.isSafeHref(o))&&(r[e]=o);Object.keys(r).length>0&&(e.attrs=r)}return e});t.length>0&&(S.marks=t)}if("pageLink"===y&&f.attrs){const t=f.attrs.pageId;"string"==typeof t&&t.length>0&&m.add(t)}if(("image"===y||"mindMapTopic"===y)&&f.attrs){const t=f.attrs.attachmentId;"string"==typeof t&&t.length>0&&g.add(t)}if("fileAttachment"===y&&f.attrs){const t=f.attrs.attachmentId;"string"==typeof t&&t.length>0&&g.add(t)}if(("mathInline"===y||"mathBlock"===y)&&f.attrs){const e=f.attrs.latex;if("string"==typeof e&&e.length>5e3)throw t.validation("公式内容过长(上限 5000 字符)")}if("mention"===y&&f.attrs){const t=f.attrs.userId;"string"==typeof t&&t.length>0&&u.add(t)}return Array.isArray(f.content)&&(S.content=f.content.filter(t=>null!=t).map(t=>walk(t,l+1,"string"==typeof y?y:""))),S};return{doc:walk(i,0),schemaVersion:SCHEMA_VERSION,linkTargetIds:[...m],attachmentIds:[...g],mentionUserIds:[...u]}}isSafeHref(t){const e=t.trim().toLowerCase();return!(!e.startsWith("/")&&!e.startsWith("#"))||e.startsWith("http://")||e.startsWith("https://")||e.startsWith("mailto:")}extractPlainText(t){if(l(t))return f(t);const e=[],walk=t=>{if("object"!=typeof t||null===t)return;const n=t;"string"==typeof n.text&&e.push(n.text),Array.isArray(n.content)&&n.content.forEach(walk)};return walk(t),e.join(" ").replace(/\s+/g," ").trim()}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export*from"../shared/video-embed.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export*from"../shared/workbook.js";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { ContentSchemaService, SCHEMA_VERSION } from './validation/schema.js';
|
|
2
|
+
export type { ValidatedDoc } from './validation/schema.js';
|
|
3
|
+
export { ContentValidationError } from './validation/error.js';
|
|
4
|
+
export { ALLOWED_NODES, ALLOWED_MARKS } from './validation/doc-node-whitelist.js';
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface InlineAnchor {
|
|
2
|
+
quote: string;
|
|
3
|
+
prefix?: string | null;
|
|
4
|
+
suffix?: string | null;
|
|
5
|
+
start?: number | null;
|
|
6
|
+
}
|
|
7
|
+
export interface ResolvedRange {
|
|
8
|
+
from: number;
|
|
9
|
+
to: number;
|
|
10
|
+
score: number;
|
|
11
|
+
exact: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare const ANCHOR_CONTEXT_LENGTH = 64;
|
|
14
|
+
export declare const ANCHOR_QUOTE_MAX = 500;
|
|
15
|
+
export interface FlatSegment {
|
|
16
|
+
start: number;
|
|
17
|
+
blockIndex: number;
|
|
18
|
+
length: number;
|
|
19
|
+
}
|
|
20
|
+
export interface FlatDocText {
|
|
21
|
+
text: string;
|
|
22
|
+
segments: FlatSegment[];
|
|
23
|
+
}
|
|
24
|
+
export declare function flattenDocText(blocks: string[]): FlatDocText;
|
|
25
|
+
export declare function locateBlock(flat: FlatDocText, charOffset: number): {
|
|
26
|
+
blockIndex: number;
|
|
27
|
+
offsetInBlock: number;
|
|
28
|
+
} | null;
|
|
29
|
+
export declare function resolveAnchor(text: string, anchor: InlineAnchor): ResolvedRange | null;
|
|
30
|
+
export declare function commonSuffixLength(a: string, b: string): number;
|
|
31
|
+
export declare function commonPrefixLength(a: string, b: string): number;
|
|
32
|
+
export declare function clipContext(text: string): string;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export declare const CALLOUT_TONES: readonly ["info", "success", "warning", "danger"];
|
|
2
|
+
export declare const CALLOUT_ICONS: readonly ["💡", "ℹ️", "✅", "⚠️", "❗", "📌", "🎯", "💬", "📖", "📝", "🚀", "🔔", "🔎", "⭐", "🌅", "🛠️", "📅", "🔒", "❤️", "🎉", "❓", "📎", "🏁", "🔥"];
|
|
3
|
+
export declare const CALLOUT_COLORS: readonly ["gray", "red", "orange", "yellow", "green", "blue", "purple"];
|
|
4
|
+
export declare const CALLOUT_FILLS: readonly ["gray", "red", "orange", "yellow", "green", "blue", "purple", ...string[]];
|
|
5
|
+
export interface CalloutAttrs {
|
|
6
|
+
tone?: string | null;
|
|
7
|
+
icon?: string | null;
|
|
8
|
+
textColor?: string | null;
|
|
9
|
+
borderColor?: string | null;
|
|
10
|
+
backgroundColor?: string | null;
|
|
11
|
+
}
|
|
12
|
+
export declare function validCalloutAttrs(raw: unknown): boolean;
|
|
13
|
+
export declare function effectiveCallout(attrs?: CalloutAttrs): {
|
|
14
|
+
tone: string;
|
|
15
|
+
icon: string;
|
|
16
|
+
textColor: string | null | undefined;
|
|
17
|
+
borderColor: string;
|
|
18
|
+
backgroundColor: string;
|
|
19
|
+
};
|
|
20
|
+
export declare function calloutDOMAttributes(attrs?: CalloutAttrs): Record<string, string>;
|
|
21
|
+
export declare function calloutMetadata(attrs: CalloutAttrs): string;
|
|
22
|
+
export declare function readCalloutMetadata(text: string): {
|
|
23
|
+
attrs: CalloutAttrs;
|
|
24
|
+
length: number;
|
|
25
|
+
} | null;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export declare const CODE_THEMES: readonly [{
|
|
2
|
+
readonly value: "auto";
|
|
3
|
+
readonly label: "跟随页面";
|
|
4
|
+
}, {
|
|
5
|
+
readonly value: "paper";
|
|
6
|
+
readonly label: "纸白";
|
|
7
|
+
}, {
|
|
8
|
+
readonly value: "soft";
|
|
9
|
+
readonly label: "柔和";
|
|
10
|
+
}, {
|
|
11
|
+
readonly value: "night";
|
|
12
|
+
readonly label: "深色";
|
|
13
|
+
}];
|
|
14
|
+
export type CodeTheme = typeof CODE_THEMES[number]['value'];
|
|
15
|
+
export interface CodeBlockDisplay {
|
|
16
|
+
collapsed: boolean;
|
|
17
|
+
lineNumbers: boolean;
|
|
18
|
+
limited: boolean;
|
|
19
|
+
theme: CodeTheme;
|
|
20
|
+
}
|
|
21
|
+
export declare function codeBlockDisplay(): CodeBlockDisplay;
|
|
22
|
+
export declare function rememberCodeTheme(_theme: CodeTheme): void;
|
|
23
|
+
export declare function codeLineCount(text: string): number;
|
|
24
|
+
export declare function codeLineNumbers(text: string): string;
|
|
25
|
+
export declare function codeBlockClass(display: CodeBlockDisplay): string[];
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface CodeLanguage {
|
|
2
|
+
value: string;
|
|
3
|
+
label: string;
|
|
4
|
+
}
|
|
5
|
+
export declare const CODE_LANGUAGES: CodeLanguage[];
|
|
6
|
+
export declare function codeLanguageLabel(value: string | null | undefined): string;
|
|
7
|
+
export declare function isKnownCodeLanguage(value: string): boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function supportsDocument(raw: unknown, version?: number): boolean;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ProseMirrorNode } from './types.js';
|
|
2
|
+
export interface BlockText {
|
|
3
|
+
index: number;
|
|
4
|
+
text: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function extractBlockTexts(doc: ProseMirrorNode | null | undefined): BlockText[];
|
|
7
|
+
export declare function plainTextOf(node: ProseMirrorNode): string;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
export declare const TEXT_COLORS: readonly [{
|
|
2
|
+
readonly name: "默认";
|
|
3
|
+
readonly value: null;
|
|
4
|
+
readonly token: null;
|
|
5
|
+
}, {
|
|
6
|
+
readonly name: "灰";
|
|
7
|
+
readonly value: "#868e96";
|
|
8
|
+
readonly token: "gray";
|
|
9
|
+
}, {
|
|
10
|
+
readonly name: "红";
|
|
11
|
+
readonly value: "#d92c2c";
|
|
12
|
+
readonly token: "red";
|
|
13
|
+
}, {
|
|
14
|
+
readonly name: "橙";
|
|
15
|
+
readonly value: "#e07b1a";
|
|
16
|
+
readonly token: "orange";
|
|
17
|
+
}, {
|
|
18
|
+
readonly name: "黄褐";
|
|
19
|
+
readonly value: "#9a7b12";
|
|
20
|
+
readonly token: "yellow";
|
|
21
|
+
}, {
|
|
22
|
+
readonly name: "绿";
|
|
23
|
+
readonly value: "#2f9e44";
|
|
24
|
+
readonly token: "green";
|
|
25
|
+
}, {
|
|
26
|
+
readonly name: "蓝";
|
|
27
|
+
readonly value: "#1971c2";
|
|
28
|
+
readonly token: "blue";
|
|
29
|
+
}, {
|
|
30
|
+
readonly name: "紫";
|
|
31
|
+
readonly value: "#7048e8";
|
|
32
|
+
readonly token: "purple";
|
|
33
|
+
}];
|
|
34
|
+
export declare const HIGHLIGHT_COLORS: readonly [{
|
|
35
|
+
readonly name: "无";
|
|
36
|
+
readonly value: null;
|
|
37
|
+
readonly token: null;
|
|
38
|
+
}, {
|
|
39
|
+
readonly name: "浅灰";
|
|
40
|
+
readonly value: "#f1f3f5";
|
|
41
|
+
readonly token: "gray-soft";
|
|
42
|
+
}, {
|
|
43
|
+
readonly name: "浅红";
|
|
44
|
+
readonly value: "#ffe3e3";
|
|
45
|
+
readonly token: "red";
|
|
46
|
+
}, {
|
|
47
|
+
readonly name: "浅橙";
|
|
48
|
+
readonly value: "#fff0db";
|
|
49
|
+
readonly token: "orange";
|
|
50
|
+
}, {
|
|
51
|
+
readonly name: "浅黄";
|
|
52
|
+
readonly value: "#fff3bf";
|
|
53
|
+
readonly token: "yellow";
|
|
54
|
+
}, {
|
|
55
|
+
readonly name: "浅绿";
|
|
56
|
+
readonly value: "#d3f9d8";
|
|
57
|
+
readonly token: "green";
|
|
58
|
+
}, {
|
|
59
|
+
readonly name: "浅蓝";
|
|
60
|
+
readonly value: "#d0ebff";
|
|
61
|
+
readonly token: "blue";
|
|
62
|
+
}, {
|
|
63
|
+
readonly name: "浅紫";
|
|
64
|
+
readonly value: "#e5dbff";
|
|
65
|
+
readonly token: "purple";
|
|
66
|
+
}, {
|
|
67
|
+
readonly name: "灰";
|
|
68
|
+
readonly value: "#dee2e6";
|
|
69
|
+
readonly token: "gray";
|
|
70
|
+
}, {
|
|
71
|
+
readonly name: "深灰";
|
|
72
|
+
readonly value: "#ced4da";
|
|
73
|
+
readonly token: "gray-strong";
|
|
74
|
+
}, {
|
|
75
|
+
readonly name: "红";
|
|
76
|
+
readonly value: "#ffc9c9";
|
|
77
|
+
readonly token: "red-strong";
|
|
78
|
+
}, {
|
|
79
|
+
readonly name: "橙";
|
|
80
|
+
readonly value: "#ffd8a8";
|
|
81
|
+
readonly token: "orange-strong";
|
|
82
|
+
}, {
|
|
83
|
+
readonly name: "黄";
|
|
84
|
+
readonly value: "#ffec99";
|
|
85
|
+
readonly token: "yellow-strong";
|
|
86
|
+
}, {
|
|
87
|
+
readonly name: "绿";
|
|
88
|
+
readonly value: "#b2f2bb";
|
|
89
|
+
readonly token: "green-strong";
|
|
90
|
+
}, {
|
|
91
|
+
readonly name: "蓝";
|
|
92
|
+
readonly value: "#a5d8ff";
|
|
93
|
+
readonly token: "blue-strong";
|
|
94
|
+
}, {
|
|
95
|
+
readonly name: "紫";
|
|
96
|
+
readonly value: "#d0bfff";
|
|
97
|
+
readonly token: "purple-strong";
|
|
98
|
+
}];
|
|
99
|
+
export declare function paletteColor(value: string, palette: readonly {
|
|
100
|
+
value: string | null;
|
|
101
|
+
}[]): string | null;
|
|
102
|
+
export declare function documentColorAttributes(kind: 'text' | 'highlight', value: unknown): Record<string, string>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { CommentItem, ProseMirrorNode } from './types.js';
|
|
2
|
+
export type InlineMarkState = 'exact' | 'shifted';
|
|
3
|
+
export interface InlineMark {
|
|
4
|
+
commentId: string;
|
|
5
|
+
textIndex: number;
|
|
6
|
+
from: number;
|
|
7
|
+
to: number;
|
|
8
|
+
state: InlineMarkState;
|
|
9
|
+
hasReplies: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface ResolvedInlineComments {
|
|
12
|
+
marks: InlineMark[];
|
|
13
|
+
orphanCommentIds: string[];
|
|
14
|
+
byTextIndex: Map<number, InlineMark[]>;
|
|
15
|
+
}
|
|
16
|
+
export interface TextRun {
|
|
17
|
+
index: number;
|
|
18
|
+
text: string;
|
|
19
|
+
flatStart: number;
|
|
20
|
+
flatEnd: number;
|
|
21
|
+
}
|
|
22
|
+
export declare function buildTextRuns(doc: ProseMirrorNode | null | undefined): TextRun[];
|
|
23
|
+
export declare function buildFlatText(doc: ProseMirrorNode | null | undefined): string;
|
|
24
|
+
export declare function resolveInlineComments(doc: ProseMirrorNode | null | undefined, comments: CommentItem[]): ResolvedInlineComments;
|
|
25
|
+
export interface TextPiece {
|
|
26
|
+
text: string;
|
|
27
|
+
marks: InlineMark[];
|
|
28
|
+
}
|
|
29
|
+
export declare function splitTextByMarks(text: string, marks: InlineMark[]): TextPiece[];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type MindMapJson, type MindMapModel } from './mind-map.js';
|
|
2
|
+
export declare const MIND_ICONS: string[];
|
|
3
|
+
export declare const MIND_COLORS: string[];
|
|
4
|
+
export declare const MIND_BACKGROUNDS: string[];
|
|
5
|
+
export declare function topicTitle(node: MindMapJson): string;
|
|
6
|
+
export declare function topicField(node: MindMapJson, field: 'description' | 'note'): string;
|
|
7
|
+
export declare function branchModel(model: MindMapModel, id: string): MindMapModel;
|
|
8
|
+
export declare function ancestorIds(model: MindMapModel, id: string): string[];
|
|
9
|
+
export interface MindSearchHit {
|
|
10
|
+
id: string;
|
|
11
|
+
field: 'title' | 'description' | 'note';
|
|
12
|
+
from: number;
|
|
13
|
+
to: number;
|
|
14
|
+
}
|
|
15
|
+
export declare function searchMindMap(model: MindMapModel, query: string, details?: boolean): MindSearchHit[];
|
|
16
|
+
export declare function parseMindMapImport(raw: string): MindMapModel;
|
|
17
|
+
export declare function mindMapTemplate(kind: 'project' | 'meeting' | 'knowledge'): MindMapModel;
|
|
18
|
+
export declare function importBranch(model: MindMapModel): MindMapJson[];
|
|
19
|
+
export declare function parseMindBranch(raw: string): MindMapJson[];
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
export declare const MIND_MAP_VERSION = 3;
|
|
2
|
+
export declare const MIND_MAP_MAX_TOPICS = 500;
|
|
3
|
+
export declare const MIND_MAP_MAX_TEXT = 2000;
|
|
4
|
+
export declare const MIND_MAP_LAYOUTS: readonly ["right", "left", "both", "down"];
|
|
5
|
+
export declare const MIND_MAP_THEMES: readonly ["blue", "teal", "violet", "amber"];
|
|
6
|
+
export type MindMapLayout = typeof MIND_MAP_LAYOUTS[number];
|
|
7
|
+
export type MindMapTheme = typeof MIND_MAP_THEMES[number];
|
|
8
|
+
export interface MindMark {
|
|
9
|
+
type: string;
|
|
10
|
+
attrs?: Record<string, unknown>;
|
|
11
|
+
}
|
|
12
|
+
export interface MindRun {
|
|
13
|
+
text: string;
|
|
14
|
+
marks?: MindMark[];
|
|
15
|
+
}
|
|
16
|
+
export interface MindRelation {
|
|
17
|
+
id: string;
|
|
18
|
+
source: string;
|
|
19
|
+
target: string;
|
|
20
|
+
kind: 'related' | 'depends' | 'reference';
|
|
21
|
+
label: string;
|
|
22
|
+
}
|
|
23
|
+
export interface MindTopic {
|
|
24
|
+
id: string;
|
|
25
|
+
parentId: string;
|
|
26
|
+
order: number;
|
|
27
|
+
text: string;
|
|
28
|
+
runs?: MindRun[];
|
|
29
|
+
description?: string;
|
|
30
|
+
note?: string;
|
|
31
|
+
shape?: 'round' | 'pill' | 'text';
|
|
32
|
+
color?: string;
|
|
33
|
+
background?: string;
|
|
34
|
+
fontSize?: number;
|
|
35
|
+
align?: 'left' | 'center' | 'right';
|
|
36
|
+
icon?: string;
|
|
37
|
+
done?: boolean;
|
|
38
|
+
attachmentId?: string;
|
|
39
|
+
side?: -1 | 1;
|
|
40
|
+
}
|
|
41
|
+
export interface MindMapModel {
|
|
42
|
+
id: string;
|
|
43
|
+
rootId: string;
|
|
44
|
+
layout: MindMapLayout;
|
|
45
|
+
theme: MindMapTheme;
|
|
46
|
+
height: number;
|
|
47
|
+
topics: MindTopic[];
|
|
48
|
+
relations?: MindRelation[];
|
|
49
|
+
branchStyle?: 'curve' | 'straight' | 'step';
|
|
50
|
+
}
|
|
51
|
+
export interface MindMapViewState {
|
|
52
|
+
openId: string | null;
|
|
53
|
+
maps: Record<string, {
|
|
54
|
+
selectedId: string;
|
|
55
|
+
selectedIds?: string[];
|
|
56
|
+
showGrid?: boolean;
|
|
57
|
+
showQuickControls?: boolean;
|
|
58
|
+
panMode?: boolean;
|
|
59
|
+
collapsed: string[];
|
|
60
|
+
viewport: {
|
|
61
|
+
x: number;
|
|
62
|
+
y: number;
|
|
63
|
+
zoom: number;
|
|
64
|
+
};
|
|
65
|
+
}>;
|
|
66
|
+
}
|
|
67
|
+
export interface MindMapJson {
|
|
68
|
+
type?: string;
|
|
69
|
+
attrs?: Record<string, unknown>;
|
|
70
|
+
text?: string;
|
|
71
|
+
marks?: MindMark[];
|
|
72
|
+
content?: MindMapJson[];
|
|
73
|
+
}
|
|
74
|
+
export interface PlacedTopic extends MindTopic {
|
|
75
|
+
x: number;
|
|
76
|
+
y: number;
|
|
77
|
+
width: number;
|
|
78
|
+
height: number;
|
|
79
|
+
lines: string[];
|
|
80
|
+
side: -1 | 1;
|
|
81
|
+
}
|
|
82
|
+
export interface MindMapDrawing {
|
|
83
|
+
nodes: PlacedTopic[];
|
|
84
|
+
edges: Array<{
|
|
85
|
+
id: string;
|
|
86
|
+
source: string;
|
|
87
|
+
target: string;
|
|
88
|
+
path: string;
|
|
89
|
+
}>;
|
|
90
|
+
bounds: {
|
|
91
|
+
x: number;
|
|
92
|
+
y: number;
|
|
93
|
+
width: number;
|
|
94
|
+
height: number;
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
export declare const mindMapId: () => `${string}-${string}-${string}-${string}-${string}`;
|
|
98
|
+
export declare function createMindMap(layout?: MindMapLayout): MindMapJson;
|
|
99
|
+
export declare function readMindMap(node: MindMapJson | null | undefined): MindMapModel | null;
|
|
100
|
+
export declare function mindMapTree(model: MindMapModel): MindTopic[];
|
|
101
|
+
export declare function mindMapDescendants(model: MindMapModel, id: string): Set<string>;
|
|
102
|
+
export declare function canMoveMindTopic(model: MindMapModel, id: string, parentId: string): boolean;
|
|
103
|
+
export declare function mindMapBranchSides(model: MindMapModel): Map<string, -1 | 1>;
|
|
104
|
+
export declare function projectMindSideMove(model: MindMapModel, id: string, side: -1 | 1, beforeId?: string): MindMapModel | null;
|
|
105
|
+
export declare function textDifference(before: string, after: string): {
|
|
106
|
+
from: number;
|
|
107
|
+
to: number;
|
|
108
|
+
text: string;
|
|
109
|
+
};
|
|
110
|
+
export declare function layoutMindMap(model: MindMapModel, collapsed?: ReadonlySet<string>): MindMapDrawing;
|
|
111
|
+
export declare const MIND_MAP_PALETTE: Record<MindMapTheme, string>;
|
|
112
|
+
export declare function mindMapSvg(model: MindMapModel, options?: {
|
|
113
|
+
transparent?: boolean;
|
|
114
|
+
images?: Record<string, string>;
|
|
115
|
+
}): string;
|
|
116
|
+
export declare function mindMapOutline(model: MindMapModel): Array<MindTopic & {
|
|
117
|
+
depth: number;
|
|
118
|
+
}>;
|
|
119
|
+
export declare function mindMapMarkdown(model: MindMapModel): string;
|
|
120
|
+
export declare function mindRelationPaths(model: MindMapModel, drawing: MindMapDrawing): {
|
|
121
|
+
path: string;
|
|
122
|
+
x: number;
|
|
123
|
+
y: number;
|
|
124
|
+
id: string;
|
|
125
|
+
source: string;
|
|
126
|
+
target: string;
|
|
127
|
+
kind: "related" | "depends" | "reference";
|
|
128
|
+
label: string;
|
|
129
|
+
}[];
|
|
130
|
+
export declare function mindMapToJson(model: MindMapModel, fresh?: boolean): MindMapJson;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare const MIND_TOPIC_LINE_HEIGHT = 22;
|
|
2
|
+
export declare const mindTopicLineHeight: (fontSize?: number) => number;
|
|
3
|
+
export declare const MIND_TOPIC_PADDING = 34;
|
|
4
|
+
export declare const MIND_TOPIC_FONT = "-apple-system, BlinkMacSystemFont, \"Segoe UI\", \"PingFang SC\", \"Hiragino Sans GB\", \"Microsoft YaHei\", sans-serif";
|
|
5
|
+
export type MindTextMeasure = (text: string) => number;
|
|
6
|
+
declare let factory: ((fontSize: number, weight: number) => MindTextMeasure | null) | undefined;
|
|
7
|
+
export declare function setMindTextMeasureFactory(value: typeof factory): void;
|
|
8
|
+
export declare function mindTextMeasure(fontSize?: number, weight?: number): MindTextMeasure;
|
|
9
|
+
export declare function wrapMindText(text: string, width: number, measure: MindTextMeasure): string[];
|
|
10
|
+
export declare function mindTopicTextSize(text: string, fontSize?: number, root?: boolean, description?: string, bold?: boolean, minimumWidth?: number): {
|
|
11
|
+
width: number;
|
|
12
|
+
lines: string[];
|
|
13
|
+
descriptionLines: string[];
|
|
14
|
+
titleHeight: number;
|
|
15
|
+
};
|
|
16
|
+
export {};
|