Skip to content

Commit be2cf1c

Browse files
authored
Add files via upload
1 parent 87d0cd5 commit be2cf1c

3 files changed

Lines changed: 72 additions & 10 deletions

File tree

app.js

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1717,10 +1717,17 @@ function parseCountedToken(raw) {
17171717
return { value: token, count: 1 };
17181718
}
17191719

1720+
function normalizeImportedPatternText(text) {
1721+
return String(text || "")
1722+
.replace(/[]/g, ",")
1723+
.replace(/[]/g, "(")
1724+
.replace(/[]/g, ")");
1725+
}
1726+
17201727
function parseImportedItems(text) {
17211728
const items = [];
17221729
const unresolved = [];
1723-
splitPatternTokens(text).forEach((token) => {
1730+
splitPatternTokens(normalizeImportedPatternText(text)).forEach((token) => {
17241731
const { value, count } = parseCountedToken(token);
17251732
const groupMatch = value.match(/^[\(](.+)[\)]$/);
17261733
if (groupMatch) {
@@ -1729,7 +1736,14 @@ function parseImportedItems(text) {
17291736
const parsed = parseCountedToken(innerToken);
17301737
const stitch = findStitch(parsed.value);
17311738
if (!stitch) {
1732-
unresolved.push(innerToken);
1739+
const compactItems = splitCompactStitches(parsed.value);
1740+
if (compactItems.length) {
1741+
compactItems.forEach((item) => {
1742+
groupItems.push({ ...item, count: item.count * parsed.count });
1743+
});
1744+
} else {
1745+
unresolved.push(innerToken);
1746+
}
17331747
return;
17341748
}
17351749
groupItems.push({ stitchId: stitch.id, count: parsed.count });
@@ -1739,7 +1753,12 @@ function parseImportedItems(text) {
17391753
}
17401754
const stitch = findStitch(value);
17411755
if (!stitch) {
1742-
unresolved.push(token);
1756+
const compactItems = splitCompactStitches(value);
1757+
if (compactItems.length) {
1758+
items.push({ type: "group", groupName: value, count, items: compactItems });
1759+
} else {
1760+
unresolved.push(token);
1761+
}
17431762
return;
17441763
}
17451764
items.push({ stitchId: stitch.id, count });
@@ -1770,8 +1789,12 @@ function parseTextPattern(text) {
17701789
if (/^-{3,}$/.test(raw)) return;
17711790
const match = raw.match(/^(?:R|)?\s*(\d+)(?:\s*[-~]\s*(\d+))?(?:\s*[])?(?::||\s+)\s*(.+)$/i);
17721791
if (!match) {
1773-
if (/^[\u4e00-\u9fa5A-Za-z][\u4e00-\u9fa5A-Za-z0-9\s_-]*$/.test(raw)) {
1774-
currentPart = { id: crypto.randomUUID(), name: raw, notes: "", segments: [] };
1792+
const [partNameText, ...partNoteParts] = raw.split("//");
1793+
const partName = partNameText.trim();
1794+
const partNote = partNoteParts.join("//").trim();
1795+
const titleMatch = partName.match(/^[\u4e00-\u9fa5A-Za-z][\u4e00-\u9fa5A-Za-z0-9\s_*\-×xX]*$/);
1796+
if (titleMatch) {
1797+
currentPart = { id: crypto.randomUUID(), name: partName, notes: partNote, segments: [] };
17751798
parts.push(currentPart);
17761799
} else {
17771800
unparsed.push(`第 ${index + 1} 行:${raw}`);
@@ -1780,15 +1803,17 @@ function parseTextPattern(text) {
17801803
}
17811804
const start = Number(match[1]);
17821805
const end = Math.max(start, Number(match[2] || start));
1783-
const parsed = parseImportedItems(match[3]);
1806+
const [stitchText, ...noteParts] = match[3].split("//");
1807+
const note = noteParts.join("//").trim();
1808+
const parsed = parseImportedItems(stitchText);
17841809
if (!parsed.items.length) {
17851810
unparsed.push(`第 ${index + 1} 行:${raw}`);
17861811
return;
17871812
}
17881813
parsed.unresolved.forEach((item) => unparsed.push(`第 ${index + 1} 行無法辨識:${item}`));
17891814
const part = ensurePart();
17901815
for (let round = start; round <= end; round += 1) {
1791-
part.segments.push({ id: crypto.randomUUID(), repeat: 1, note: "", items: structuredClone(parsed.items) });
1816+
part.segments.push({ id: crypto.randomUUID(), repeat: 1, note, items: structuredClone(parsed.items) });
17921817
}
17931818
});
17941819
const validParts = parts.filter((part) => part.segments.length);
@@ -1801,6 +1826,24 @@ function findStitch(value) {
18011826
return state.stitches.find((stitch) => [stitch.id, stitch.zh, stitch.letter].some((item) => String(item).toLowerCase() === normalized));
18021827
}
18031828

1829+
function splitCompactStitches(value) {
1830+
const text = String(value || "").trim();
1831+
if (!/^[A-Za-z]+$/.test(text)) return [];
1832+
const letters = state.stitches
1833+
.map((stitch) => ({ id: stitch.id, letter: String(stitch.letter || "").trim() }))
1834+
.filter((item) => item.letter && /^[A-Za-z]+$/.test(item.letter))
1835+
.sort((a, b) => b.letter.length - a.letter.length);
1836+
const result = [];
1837+
let index = 0;
1838+
while (index < text.length) {
1839+
const match = letters.find((item) => text.slice(index, index + item.letter.length).toLowerCase() === item.letter.toLowerCase());
1840+
if (!match) return [];
1841+
result.push({ stitchId: match.id, count: 1 });
1842+
index += match.letter.length;
1843+
}
1844+
return result.length > 1 ? result : [];
1845+
}
1846+
18041847
function expandedRows(pattern) {
18051848
const rows = [];
18061849
pattern.parts.forEach((part) => {
@@ -5804,6 +5847,24 @@ document.querySelectorAll(".modal").forEach((modal) => {
58045847
});
58055848
});
58065849

5850+
function selectNumericInput(input) {
5851+
if (!(input instanceof HTMLInputElement)) return;
5852+
if (input.type !== "number" || input.disabled || input.readOnly) return;
5853+
requestAnimationFrame(() => {
5854+
try {
5855+
input.select();
5856+
} catch {}
5857+
});
5858+
}
5859+
5860+
document.addEventListener("focusin", (event) => {
5861+
selectNumericInput(event.target);
5862+
});
5863+
5864+
document.addEventListener("pointerup", (event) => {
5865+
if (event.target === document.activeElement) selectNumericInput(event.target);
5866+
});
5867+
58075868
registerServiceWorker();
58085869
if ("scrollRestoration" in history) history.scrollRestoration = "manual";
58095870
requestAnimationFrame(() => {

index.html

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -748,7 +748,8 @@ <h2>文字匯入織圖</h2>
748748
<details class="reference-box">
749749
<summary>參考針法與格式</summary>
750750
<p>每個中文標題會建立一個部分;部分下面可輸入 R1、R2 或 R3-9。</p>
751-
<p>例:身體 / R1 6X,V,A / R2 3(2X,A,2X) / 手 / R1 12X。</p>
751+
<p>例:身體 // 主體備註 / R1 6X,V,A // 膚色環起 / R2 3(2X,A,2X) / 手 / R1 12X。</p>
752+
<p>使用 // 可加入備註;放在部分標題後是部分備註,放在 R1 後是段落備註。</p>
752753
<p>可用格式:6X、X x 6、3(2X,A,2X)、2(5X,W),也可用逗號分隔針法。</p>
753754
<p id="textPatternReference"></p>
754755
</details>
@@ -758,7 +759,7 @@ <h2>文字匯入織圖</h2>
758759
</label>
759760
<label class="wide-field">
760761
貼上文字織圖
761-
<textarea id="textPatternInput" rows="8" placeholder="身體&#10;R1 6X,V,A&#10;R2 3(2X,A,2X)&#10;R3-9 V,2(5X,W),5X,X&#10;手&#10;R1 12X&#10;R2 2X,A,10V"></textarea>
762+
<textarea id="textPatternInput" rows="8" placeholder="身體 // 主體備註&#10;R1 6X,V,A // 膚色環起&#10;R2 3(2X,A,2X)&#10;R3-9 V,2(5X,W),5X,X // 不加不減&#10;手&#10;R1 12X&#10;R2 2X,A,10V"></textarea>
762763
</label>
763764
<button class="primary-button full-width" id="convertTextPatternBtn">匯入成織圖</button>
764765
<div class="unparsed-list hidden" id="textPatternUnparsed"></div>

service-worker.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const VERSION = "118";
1+
const VERSION = "123";
22
const CACHE_NAME = `free-knit-workbench-v${VERSION}`;
33
const ASSETS = [
44
"./",

0 commit comments

Comments
 (0)