import fs from 'node:fs';import crypto from 'node:crypto'; const b='work/trilium',out=b+'/phase4';fs.mkdirSync(out,{recursive:true}); const data=JSON.parse(fs.readFileSync(b+'/phase4-inspect/data.json','utf8')); const hash=s=>crypto.createHash('sha256').update(s).digest('hex'); const names={text:'텍스트',code:'코드',file:'파일',image:'이미지',search:'저장된 검색',book:'책',relationMap:'관계도',noteMap:'노트 지도',render:'렌더링 노트',mermaid:'Mermaid 도표',canvas:'캔버스',webView:'웹 보기',mindMap:'마인드맵',geoMap:'지리 지도',doc:'도움말',launcher:'실행 도구'}; const changes=data.notes.filter(n=>n.content).map(n=>{ let content=n.content; if(n.noteId==='EW0u2LoAZTYv')content=content.replace('addRow(count.type,','addRow(('+JSON.stringify(names)+')[count.type] || count.type,'); if(n.noteId==='WJaFVVj6QO1J')content=content.replace('name: nc.type,','name: ('+JSON.stringify(names)+')[nc.type] || nc.type,'); if(n.noteId==='ga590IKu7zmy')content=content.replace("'DONE'","'완료'").replace("'TODO'","'할 일'"); if(content===n.content)throw Error('Unchanged '+n.noteId); return {noteId:n.noteId,oldTitle:n.title,title:n.title,oldContentSha256:hash(n.content),content}; }); const aliases={weight:'체중',geolocation:'위치 좌표',readingEnd:'독서 종료일',readingStart:'독서 시작일',author:'저자',link:'링크',location:'장소',tag:'태그',todoDate:'예정일',doneDate:'완료일'}; const attributes=data.attributes.map(a=>({...a,oldValue:a.value,value:a.value.replace(/,alias=[^,]*/,'')+',alias='+aliases[a.name.slice(6)]})); const prefixes={TODO:'할 일',DONE:'완료','28. 11. 2017':'2017년 11월 28일'}; const branches=data.branches.map(r=>({...r,oldPrefix:r.prefix,prefix:prefixes[r.prefix]})); fs.writeFileSync(out+'/korean-phase4-pending.json',JSON.stringify({version:4,changes,attributes,branches},null,2)); const known={'런치바 템플릿':'실행 도구 템플릿','기본 추상 런처':'실행 도구 기본 템플릿','명령 런처':'명령 실행 도구','노트 런처':'노트 실행 도구','스크립트 런처':'스크립트 실행 도구','런치바':'실행 도구 모음','사용 가능한 런처':'사용 가능한 실행 도구','표시된 런처':'표시할 실행 도구','캘린더':'달력','최근 변경 내역':'최근 변경 사항','노트와 채팅':'노트와 대화','옵션':'설정','외관':'모양','MFA':'다중 인증','사용자 가이드':'사용자 안내서','사용자 숨김':'사용자 숨김 영역','Mobile Launch Bar':'모바일 실행 도구 모음'}; const server=JSON.parse(fs.readFileSync(b+'/server.ko.json','utf8')); for(const [key,value]of Object.entries(server['hidden-subtree']))if(known[value])server['hidden-subtree'][key]=known[value]; fs.writeFileSync(out+'/server.json',JSON.stringify(server,null,2)+'\n'); let main=fs.readFileSync(b+'/phase3/main.cjs','utf8');const oldmain=main; const replace=(from,to)=>{if(main.split(from).length!==2)throw Error('Patch point mismatch '+from);main=main.replace(from,to);}; replace('jg=`assets/v${CR.version}-ko-20260913-r3`','jg=`assets/v${CR.version}-ko-20260913-r4`'); replace('title:"Mobile Launch Bar"','title:"모바일 실행 도구 모음"'); replace('(r.restoreNames||n.noteId.startsWith("_help_"))&&n.title!==t.title','(r.restoreNames||n.noteId.startsWith("_help_")||('+JSON.stringify(known)+')[n.title]===t.title)&&n.title!==t.title'); replace('triliumApplyKoreanNotes()})','triliumApplyKoreanNotes();triliumApplyKoreanPhase4()})'); const hook=` function triliumApplyKoreanPhase4(){ const fs=require('fs'),crypto=require('crypto'),file='/home/node/trilium-data/korean-phase4-pending.json'; if(!fs.existsSync(file))return; const plan=JSON.parse(fs.readFileSync(file,'utf8')); if(plan.version!==4)throw Error('Invalid Korean phase 4 migration'); const hash=s=>crypto.createHash('sha256').update(s).digest('hex'); de.transactional(()=>{ for(const c of plan.changes){const n=J.getNoteOrThrow(c.noteId);if(n.isDeleted||n.isProtected||n.title!==c.oldTitle||(n.getContent()!==c.content&&hash(n.getContent())!==c.oldContentSha256))throw Error('Note conflict '+c.noteId);} for(const c of plan.attributes){const a=J.getAttributeOrThrow(c.attributeId);if(a.isDeleted||a.noteId!==c.noteId||a.name!==c.name||(a.value!==c.oldValue&&a.value!==c.value))throw Error('Attribute conflict '+c.attributeId);} for(const c of plan.branches){const a=J.getBranchOrThrow(c.branchId);if(a.isDeleted||a.noteId!==c.noteId||(a.prefix!==c.oldPrefix&&a.prefix!==c.prefix))throw Error('Branch conflict '+c.branchId);} for(const c of plan.changes){const n=J.getNoteOrThrow(c.noteId);if(n.getContent()===c.content)continue;const rev=new Mk({noteId:n.noteId,title:n.title,type:n.type,mime:n.mime,isProtected:n.isProtected,utcDateLastEdited:n.utcDateModified,utcDateCreated:kr.utcNowDateTime(),utcDateModified:kr.utcNowDateTime(),dateLastEdited:n.dateModified,dateCreated:kr.localNowDateTime()},true);rev.save();rev.setContent(n.getContent());n.setContent(c.content);} for(const c of plan.attributes){const a=J.getAttributeOrThrow(c.attributeId);if(a.value!==c.value){a.value=c.value;a.save();}} for(const c of plan.branches){const a=J.getBranchOrThrow(c.branchId);if(a.prefix!==c.prefix){a.prefix=c.prefix;a.save();}} }); fs.renameSync(file,file+'.applied-'+Date.now());console.log('KOREAN_PHASE4 '+JSON.stringify({notes:plan.changes.length,attributes:plan.attributes.length,branches:plan.branches.length})); } `; fs.writeFileSync(out+'/main.cjs',hook+main); fs.writeFileSync(out+'/manifest.json',JSON.stringify({previousMainSha256:hash(oldmain),files:Object.fromEntries(['main.cjs','server.json','korean-phase4-pending.json'].map(n=>[n,hash(fs.readFileSync(out+'/'+n))]))},null,2)); console.log({notes:changes.length,attributes:attributes.length,branches:branches.length});