@@ -1856,6 +1856,7 @@ function parseImportedItems(text) {
18561856function parseTextPattern ( text ) {
18571857 const parts = [ ] ;
18581858 const unparsed = [ ] ;
1859+ const missingStitches = [ ] ;
18591860 let importedName = "" ;
18601861 let currentPart = null ;
18611862 const ensurePart = ( name = "文字匯入" ) => {
@@ -1898,14 +1899,15 @@ function parseTextPattern(text) {
18981899 return ;
18991900 }
19001901 parsed . unresolved . forEach ( ( item ) => unparsed . push ( `第 ${ index + 1 } 行無法辨識:${ item } ` ) ) ;
1902+ parsed . unresolved . forEach ( ( item ) => missingStitches . push ( `第 ${ index + 1 } 行:${ item } ` ) ) ;
19011903 const part = ensurePart ( ) ;
19021904 for ( let round = start ; round <= end ; round += 1 ) {
19031905 part . segments . push ( { id : crypto . randomUUID ( ) , repeat : 1 , note, items : structuredClone ( parsed . items ) } ) ;
19041906 }
19051907 } ) ;
19061908 const validParts = parts . filter ( ( part ) => part . segments . length ) ;
19071909 parts . filter ( ( part ) => ! part . segments . length ) . forEach ( ( part ) => unparsed . push ( `部分「${ part . name } 」沒有可辨識段落` ) ) ;
1908- return { name : importedName , parts : validParts , segments : validParts . flatMap ( ( part ) => part . segments ) , unparsed } ;
1910+ return { name : importedName , parts : validParts , segments : validParts . flatMap ( ( part ) => part . segments ) , unparsed, missingStitches } ;
19091911}
19101912
19111913function findStitch ( value ) {
@@ -1938,7 +1940,7 @@ function expandedRows(pattern) {
19381940 part . segments . forEach ( ( segment ) => {
19391941 const repeat = Math . max ( 1 , Number ( segment . repeat || 1 ) ) ;
19401942 for ( let index = 0 ; index < repeat ; index += 1 ) {
1941- rows . push ( { ...segment , partId : part . id , partName : part . name , round : partRound , label : formatRound ( partRound ) } ) ;
1943+ rows . push ( { ...segment , partId : part . id , partName : part . name , partNotes : part . notes || "" , round : partRound , label : formatRound ( partRound ) } ) ;
19421944 partRound += 1 ;
19431945 }
19441946 } ) ;
@@ -2453,9 +2455,36 @@ function stablePatternKey(pattern) {
24532455 return JSON . stringify ( scrub ( pattern ) ) ;
24542456}
24552457
2458+ function collectMissingPatternStitches ( pattern ) {
2459+ const stitchIds = new Set ( state . stitches . map ( ( stitch ) => stitch . id ) ) ;
2460+ const missing = new Set ( ) ;
2461+ const checkItems = ( items = [ ] ) => {
2462+ items . forEach ( ( item ) => {
2463+ if ( item ?. type === "group" ) {
2464+ checkItems ( item . items || [ ] ) ;
2465+ return ;
2466+ }
2467+ const stitchId = item ?. stitchId ;
2468+ if ( stitchId && ! stitchIds . has ( stitchId ) ) missing . add ( stitchId ) ;
2469+ } ) ;
2470+ } ;
2471+ ( pattern ?. parts || [ ] ) . forEach ( ( part ) => {
2472+ ( part . segments || [ ] ) . forEach ( ( segment ) => checkItems ( segment . items || [ ] ) ) ;
2473+ } ) ;
2474+ ( pattern ?. segments || [ ] ) . forEach ( ( segment ) => checkItems ( segment . items || [ ] ) ) ;
2475+ return Array . from ( missing ) ;
2476+ }
2477+
2478+ function assertPatternStitchesExist ( pattern ) {
2479+ const missing = collectMissingPatternStitches ( pattern ) ;
2480+ if ( ! missing . length ) return ;
2481+ throw new Error ( `缺少以下針法,請先到設定新增後再匯入:${ missing . join ( "、" ) } ` ) ;
2482+ }
2483+
24562484function importPatternPackage ( payload ) {
24572485 const pattern = payload ?. type === "gogo-pattern" ? payload . pattern : payload ?. pattern || payload ;
24582486 if ( ! pattern || typeof pattern !== "object" ) throw new Error ( "不是可匯入的織圖檔" ) ;
2487+ assertPatternStitchesExist ( pattern ) ;
24592488 const importKey = stablePatternKey ( pattern ) ;
24602489 const existing = templatePatterns ( ) . find ( ( item ) => item . importKey === importKey ) ;
24612490 if ( existing ) {
@@ -2496,6 +2525,10 @@ function patternItemsToText(items = []) {
24962525 return items . map ( compactItemDisplay ) . join ( "," ) ;
24972526}
24982527
2528+ function rowNotes ( row ) {
2529+ return [ row ?. partNotes , row ?. note ] . map ( ( item ) => String ( item || "" ) . trim ( ) ) . filter ( ( item , index , list ) => item && list . indexOf ( item ) === index ) ;
2530+ }
2531+
24992532function patternToText ( pattern ) {
25002533 const lines = [ `織圖:${ pattern . name || "未命名織圖" } ` ] ;
25012534 ( pattern . parts || [ ] ) . forEach ( ( part ) => {
@@ -3552,18 +3585,19 @@ function renderTracking() {
35523585 const line = document . createElement ( "div" ) ;
35533586 line . className = `pattern-line ${ rowIndex === currentIndex ? "current-line" : "" } ${ rowIndex < progress . completedRounds ? "completed" : "" } ` ;
35543587 if ( rowIndex !== currentIndex ) {
3555- line . innerHTML = `<span class="round-chip">${ escapeHtml ( patternRow . partName || "部分" ) } · ${ escapeHtml ( formatRound ( patternRow . round ) ) } </span><span>${ escapeHtml ( patternRow . note || summarizeItems ( patternRow . items ) ) } </span>` ;
3588+ const notes = rowNotes ( patternRow ) ;
3589+ line . innerHTML = `<span class="round-chip">${ escapeHtml ( patternRow . partName || "部分" ) } · ${ escapeHtml ( formatRound ( patternRow . round ) ) } </span><span>${ escapeHtml ( summarizeItems ( patternRow . items ) ) } </span>${ notes . map ( ( note ) => `<span class="pattern-note-chip">${ escapeHtml ( note ) } </span>` ) . join ( "" ) } ` ;
35563590 } else {
35573591 const summary = document . createElement ( "span" ) ;
35583592 summary . className = "pattern-summary-chip" ;
35593593 summary . textContent = summarizeItems ( patternRow . items ) ;
35603594 line . append ( summary ) ;
3561- if ( patternRow . note ) {
3595+ rowNotes ( patternRow ) . forEach ( ( text ) => {
35623596 const note = document . createElement ( "span" ) ;
35633597 note . className = "pattern-note-chip" ;
3564- note . textContent = patternRow . note ;
3598+ note . textContent = text ;
35653599 line . append ( note ) ;
3566- }
3600+ } ) ;
35673601 const breakLine = document . createElement ( "span" ) ;
35683602 breakLine . className = "pattern-line-break" ;
35693603 line . append ( breakLine ) ;
@@ -5444,6 +5478,11 @@ els.convertTextPatternBtn.addEventListener("click", () => {
54445478 const result = parseTextPattern ( els . textPatternInput . value ) ;
54455479 const name = ( els . textPatternName . value . trim ( ) || result . name || "文字匯入織圖" ) ;
54465480 if ( warnDuplicateName ( name , templatePatterns ( ) , "織圖" , ( pattern ) => pattern . name ) ) return ;
5481+ if ( result . missingStitches ?. length ) {
5482+ els . textPatternUnparsed . classList . remove ( "hidden" ) ;
5483+ els . textPatternUnparsed . innerHTML = `<strong>缺少以下針法,請先到設定新增後再匯入:</strong>${ result . missingStitches . map ( ( item ) => `<p>${ escapeHtml ( item ) } </p>` ) . join ( "" ) } ` ;
5484+ return ;
5485+ }
54475486 if ( ! result . parts . length ) {
54485487 els . textPatternUnparsed . classList . remove ( "hidden" ) ;
54495488 els . textPatternUnparsed . innerHTML = `<strong>沒有成功辨識的段落</strong>${ result . unparsed . map ( ( item ) => `<p>${ escapeHtml ( item ) } </p>` ) . join ( "" ) } ` ;
0 commit comments