Add crop controls and larger patterns

This commit is contained in:
wuyanwanwu
2026-08-14 00:01:53 +08:00
parent 6037bde760
commit 76cc35b709
15 changed files with 128 additions and 47 deletions
+8 -3
View File
@@ -45,11 +45,16 @@ button { color: inherit; }
.panel-heading h2, .pattern-toolbar h2 { margin: 0; font-size: 18px; }
.panel-heading p { margin: 5px 0 0; color: var(--muted); font-size: 12px; }
.upload-card { height: 170px; display: block; position: relative; overflow: hidden; cursor: pointer; border: 1px dashed #a5a59e; background: #ebe7dd; }
.upload-card img { width: 100%; height: 100%; object-fit: cover; display: block; }
.upload-card { width: 100%; min-height: 100px; max-height: 260px; display: block; position: relative; overflow: hidden; border: 1px dashed #a5a59e; background: #ebe7dd; }
.upload-card canvas { width: 100%; height: 100%; display: block; pointer-events: none; }
.upload-card input { position: absolute; opacity: 0; pointer-events: none; }
.crop-preview { cursor: grab; touch-action: none; }
.crop-preview:active { cursor: grabbing; }
.crop-frame { position: absolute; inset: 0; border: 2px solid rgba(255,255,255,.85); box-shadow: inset 0 0 0 1px rgba(20,30,25,.22); pointer-events: none; }
.upload-overlay { position: absolute; right: 10px; bottom: 10px; padding: 7px 10px; color: white; background: rgba(22, 75, 57, .86); font-size: 12px; }
.file-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin: 9px 0 24px; color: var(--muted); font-size: 11px; }
.crop-zoom { margin-top: 0; }
.reset-crop { width: 100%; margin: 7px 0 20px; padding: 7px; border: 1px solid var(--line); background: white; cursor: pointer; color: var(--muted); font-size: 11px; }
.field-row { display: grid; grid-template-columns: 1fr auto 1fr; align-items: end; gap: 8px; }
.field-row label, .field { display: flex; flex-direction: column; gap: 8px; }
.field-row label span, .field > span { color: var(--muted); font-size: 12px; }
@@ -149,7 +154,7 @@ input[type="range"] { accent-color: var(--green); }
.hero-badge { display: none; }
.workspace { margin-bottom: 0; grid-template-columns: 1fr; border-bottom: 0; }
.control-panel { border-right: 0; border-bottom: 1px solid var(--ink); padding: 24px 20px; }
.upload-card { height: 210px; }
.upload-card { height: auto; min-height: 160px; }
.pattern-panel { min-height: 620px; }
.pattern-toolbar { align-items: flex-start; flex-direction: column; padding: 20px; }
.zoom-control { width: 100%; }
+103 -27
View File
@@ -71,6 +71,36 @@ function nearestColor(rgb: [number, number, number], palette: BeadColor[]) {
return closest;
}
function drawFittedImage(
ctx: CanvasRenderingContext2D,
image: HTMLImageElement,
width: number,
height: number,
fitMode: "cover" | "contain",
cropZoom: number,
cropX: number,
cropY: number,
) {
const imageRatio = image.naturalWidth / image.naturalHeight;
const boxRatio = width / height;
let drawWidth = width;
let drawHeight = height;
if ((fitMode === "cover" && imageRatio > boxRatio) || (fitMode === "contain" && imageRatio < boxRatio)) {
drawHeight = height;
drawWidth = height * imageRatio;
} else {
drawWidth = width;
drawHeight = width / imageRatio;
}
drawWidth *= cropZoom;
drawHeight *= cropZoom;
const overflowX = Math.max(0, (drawWidth - width) / 2);
const overflowY = Math.max(0, (drawHeight - height) / 2);
const drawX = (width - drawWidth) / 2 + cropX * overflowX;
const drawY = (height - drawHeight) / 2 + cropY * overflowY;
ctx.drawImage(image, drawX, drawY, drawWidth, drawHeight);
}
function makeDemoImage() {
const canvas = document.createElement("canvas");
canvas.width = 640;
@@ -102,10 +132,15 @@ function makeDemoImage() {
export default function Home() {
const [sourceUrl, setSourceUrl] = useState("");
const [sourceName, setSourceName] = useState("示例:田野小屋");
const [requestedWidth, setRequestedWidth] = useState(32);
const [requestedHeight, setRequestedHeight] = useState(32);
const [gridWidth, setGridWidth] = useState(32);
const [gridHeight, setGridHeight] = useState(32);
const [colorLimit, setColorLimit] = useState(18);
const [fitMode, setFitMode] = useState<"cover" | "contain">("cover");
const [cropZoom, setCropZoom] = useState(1);
const [cropX, setCropX] = useState(0);
const [cropY, setCropY] = useState(0);
const [pixels, setPixels] = useState<Pixel[]>([]);
const [selectedCodes, setSelectedCodes] = useState<Set<string>>(new Set());
const [onlySelected, setOnlySelected] = useState(false);
@@ -118,7 +153,10 @@ export default function Home() {
const [historyQuery, setHistoryQuery] = useState("");
const [status, setStatus] = useState("示例图已准备好,可以直接转换");
const sourceImageRef = useRef<HTMLImageElement | null>(null);
const cropCanvasRef = useRef<HTMLCanvasElement | null>(null);
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const cropPreviewRef = useRef<HTMLDivElement | null>(null);
const cropDragRef = useRef<{ pointerId: number; x: number; y: number; cropX: number; cropY: number } | null>(null);
useEffect(() => {
const saved = localStorage.getItem("bead-pattern-history");
@@ -128,28 +166,20 @@ export default function Home() {
});
}, []);
const convertImage = (image = sourceImageRef.current) => {
const convertImage = (
image = sourceImageRef.current,
crop = { zoom: cropZoom, x: cropX, y: cropY },
) => {
if (!image) return;
const width = Math.max(8, Math.min(128, gridWidth));
const height = Math.max(8, Math.min(128, gridHeight));
const width = Math.max(8, Math.min(256, requestedWidth));
const height = Math.max(8, Math.min(256, requestedHeight));
const sample = document.createElement("canvas");
sample.width = width;
sample.height = height;
const ctx = sample.getContext("2d", { willReadFrequently: true })!;
ctx.fillStyle = "#f7f5ed";
ctx.fillRect(0, 0, width, height);
const imageRatio = image.naturalWidth / image.naturalHeight;
const boxRatio = width / height;
let drawWidth = width;
let drawHeight = height;
if ((fitMode === "cover" && imageRatio > boxRatio) || (fitMode === "contain" && imageRatio < boxRatio)) {
drawHeight = height;
drawWidth = height * imageRatio;
} else {
drawWidth = width;
drawHeight = width / imageRatio;
}
ctx.drawImage(image, (width - drawWidth) / 2, (height - drawHeight) / 2, drawWidth, drawHeight);
drawFittedImage(ctx, image, width, height, fitMode, crop.zoom, crop.x, crop.y);
const data = ctx.getImageData(0, 0, width, height).data;
const initialMatches: BeadColor[] = [];
const counts = new Map<string, number>();
@@ -167,6 +197,8 @@ export default function Home() {
});
setGridWidth(width);
setGridHeight(height);
setRequestedWidth(width);
setRequestedHeight(height);
setPixels(converted);
setSelectedCodes(new Set());
setStatus(`已转换为 ${width} × ${height},共 ${width * height} 颗拼豆`);
@@ -185,6 +217,23 @@ export default function Home() {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
const canvas = cropCanvasRef.current;
const image = sourceImageRef.current;
if (!canvas || !image) return;
const targetWidth = Math.max(8, Math.min(256, requestedWidth || 8));
const targetHeight = Math.max(8, Math.min(256, requestedHeight || 8));
const ratio = targetWidth / targetHeight;
const previewWidth = ratio >= 1 ? 600 : Math.max(120, Math.round(600 * ratio));
const previewHeight = ratio >= 1 ? Math.max(120, Math.round(600 / ratio)) : 600;
canvas.width = previewWidth;
canvas.height = previewHeight;
const ctx = canvas.getContext("2d")!;
ctx.fillStyle = "#f7f5ed";
ctx.fillRect(0, 0, previewWidth, previewHeight);
drawFittedImage(ctx, image, previewWidth, previewHeight, fitMode, cropZoom, cropX, cropY);
}, [sourceUrl, requestedWidth, requestedHeight, fitMode, cropZoom, cropX, cropY]);
const usedColors = useMemo(() => {
const counts = new Map<string, number>();
pixels.forEach(({ color }) => counts.set(color.code, (counts.get(color.code) ?? 0) + 1));
@@ -221,15 +270,16 @@ export default function Home() {
ctx.fillStyle = "#56605b";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
const labelEvery = cell < 10 ? 10 : cell < 16 ? 5 : 1;
for (let column = 0; column < gridWidth; column++) {
const x = ruler + column * cell;
ctx.strokeRect(x + 0.3, 0.3, cell - 0.6, ruler - 0.6);
ctx.fillText(String(column + 1), x + cell / 2, ruler / 2);
if (column === 0 || (column + 1) % labelEvery === 0) ctx.fillText(String(column + 1), x + cell / 2, ruler / 2);
}
for (let row = 0; row < gridHeight; row++) {
const y = ruler + row * cell;
ctx.strokeRect(0.3, y + 0.3, ruler - 0.6, cell - 0.6);
ctx.fillText(String(row + 1), ruler / 2, y + cell / 2);
if (row === 0 || (row + 1) % labelEvery === 0) ctx.fillText(String(row + 1), ruler / 2, y + cell / 2);
}
pixels.forEach(({ color }, index) => {
const x = ruler + (index % gridWidth) * cell;
@@ -274,12 +324,35 @@ export default function Home() {
sourceImageRef.current = image;
setSourceUrl(url);
setSourceName(file.name);
setCropZoom(1);
setCropX(0);
setCropY(0);
setStatus("图片已载入,点击“重新转换”生成图纸");
convertImage(image);
convertImage(image, { zoom: 1, x: 0, y: 0 });
};
image.src = url;
};
const handleCropPointerDown = (event: React.PointerEvent<HTMLDivElement>) => {
event.currentTarget.setPointerCapture(event.pointerId);
cropDragRef.current = { pointerId: event.pointerId, x: event.clientX, y: event.clientY, cropX, cropY };
};
const handleCropPointerMove = (event: React.PointerEvent<HTMLDivElement>) => {
const drag = cropDragRef.current;
const preview = cropPreviewRef.current;
if (!drag || drag.pointerId !== event.pointerId || !preview) return;
const rect = preview.getBoundingClientRect();
const nextX = drag.cropX + ((event.clientX - drag.x) / Math.max(1, rect.width)) * 2;
const nextY = drag.cropY + ((event.clientY - drag.y) / Math.max(1, rect.height)) * 2;
setCropX(Math.max(-1, Math.min(1, nextX)));
setCropY(Math.max(-1, Math.min(1, nextY)));
};
const handleCropPointerUp = (event: React.PointerEvent<HTMLDivElement>) => {
if (cropDragRef.current?.pointerId === event.pointerId) cropDragRef.current = null;
};
const toggleColor = (code: string) => {
setSelectedCodes((current) => {
const next = new Set(current);
@@ -339,6 +412,8 @@ export default function Home() {
setPixels(entry.codes.map((code) => ({ color: colorMap.get(code) ?? PALETTE[0] })));
setGridWidth(entry.width);
setGridHeight(entry.height);
setRequestedWidth(entry.width);
setRequestedHeight(entry.height);
setColorLimit(entry.colorLimit);
setSourceName(entry.name);
setSelectedCodes(new Set());
@@ -384,19 +459,20 @@ export default function Home() {
<section className="workspace" aria-label="拼豆图纸转换工具">
<aside className="control-panel">
<div className="panel-heading"><span>01</span><div><h2></h2><p></p></div></div>
<label className="upload-card">
{/* The source may be a local blob URL, so framework image optimization is not applicable. */}
{/* eslint-disable-next-line @next/next/no-img-element */}
{sourceUrl ? <img src={sourceUrl} alt="待转换的原图预览" /> : <span className="upload-placeholder"></span>}
<span className="upload-overlay"></span>
<input type="file" accept="image/png,image/jpeg,image/webp" onChange={handleUpload} />
</label>
<div className="upload-card crop-preview" style={{ aspectRatio: `${Math.max(8, requestedWidth || 8)} / ${Math.max(8, requestedHeight || 8)}` }} ref={cropPreviewRef} onPointerDown={handleCropPointerDown} onPointerMove={handleCropPointerMove} onPointerUp={handleCropPointerUp} onPointerCancel={handleCropPointerUp}>
<canvas ref={cropCanvasRef} aria-label="转换区域预览,可拖动调整取景" />
<span className="crop-frame" aria-hidden="true" />
<label className="upload-overlay"><input type="file" accept="image/png,image/jpeg,image/webp" onChange={handleUpload} /></label>
</div>
<p className="file-name" title={sourceName}>{sourceName}</p>
<label className="field crop-zoom"><span> <b>{cropZoom.toFixed(1)}×</b></span><input type="range" min="1" max="5" step="0.1" value={cropZoom} onChange={(e) => setCropZoom(Number(e.target.value))} /></label>
<button className="reset-crop" onClick={() => { setCropZoom(1); setCropX(0); setCropY(0); }}></button>
<div className="field-row">
<label><span></span><input type="number" min="8" max="128" value={gridWidth} onChange={(e) => setGridWidth(Number(e.target.value))} /></label>
<label><span></span><input type="number" min="8" max="256" value={requestedWidth} onChange={(e) => setRequestedWidth(Number(e.target.value))} /></label>
<span className="times">×</span>
<label><span></span><input type="number" min="8" max="128" value={gridHeight} onChange={(e) => setGridHeight(Number(e.target.value))} /></label>
<label><span></span><input type="number" min="8" max="256" value={requestedHeight} onChange={(e) => setRequestedHeight(Number(e.target.value))} /></label>
</div>
<label className="field"><span>使 <b>{colorLimit}</b></span><input type="range" min="2" max="64" value={colorLimit} onChange={(e) => setColorLimit(Number(e.target.value))} /></label>
+3 -3
View File
@@ -11,7 +11,7 @@
"name": "rolldown-runtime"
},
"app/page.tsx": {
"file": "_next/static/chunks/page-DOJToWWt.js",
"file": "_next/static/chunks/page-ZrVfYdCY.js",
"name": "page",
"src": "app/page.tsx",
"isDynamicEntry": true,
@@ -21,7 +21,7 @@
]
},
"node_modules/vinext/dist/shims/layout-segment-context.js": {
"file": "_next/static/chunks/layout-segment-context-BAGcnabH.js",
"file": "_next/static/chunks/layout-segment-context-AZriBoei.js",
"name": "layout-segment-context",
"src": "node_modules/vinext/dist/shims/layout-segment-context.js",
"isDynamicEntry": true,
@@ -32,7 +32,7 @@
]
},
"virtual:vinext-app-browser-entry": {
"file": "_next/static/chunks/index-BdxXUyhE.js",
"file": "_next/static/chunks/index-B9U9o3wW.js",
"name": "index",
"src": "virtual:vinext-app-browser-entry",
"isEntry": true,
+2 -2
View File
@@ -1,2 +1,2 @@
<!DOCTYPE html><html lang="zh-CN"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="/_next/static/css/index.1ta3inz1.css" data-rsc-css-href="/_next/static/css/index.1ta3inz1.css" data-precedence="vite-rsc/importer-resources"/><link rel="modulepreload" fetchPriority="low" href="/_next/static/chunks/index-BdxXUyhE.js"/><script src="/_next/static/chunks/rolldown-runtime-C60lm6uB.js" type="module" async=""></script><script src="/_next/static/chunks/framework-BgSIrAUN.js" type="module" async=""></script><meta name="robots" content="noindex"/><title>豆格工坊|图片转拼豆图纸</title><meta name="description" content="在浏览器中把图片转换为拼豆像素图纸,按色号、名称或像素格筛选颜色。"/><title>404: This page could not be found.</title><script>Object.assign(((self[Symbol.for("vinext.navigationRuntime")]??={bootstrap:{routeManifest:null},functions:{}}).bootstrap.rsc??={rsc:[]}),{params:{},nav:{"pathname":"/__vinext_nonexistent_for_404__","searchParams":[]}})</script><link rel="modulepreload" href="/_next/static/chunks/index-BdxXUyhE.js" />
</head><body><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><script type="module" src="/_next/static/chunks/index-BdxXUyhE.js" id="_R_" async=""></script><script>((self[Symbol.for("vinext.navigationRuntime")]??={bootstrap:{routeManifest:null},functions:{}}).bootstrap.rsc??={rsc:[]}).rsc.push("1:\"$Sreact.fragment\"\n")</script><script>((self[Symbol.for("vinext.navigationRuntime")]??={bootstrap:{routeManifest:null},functions:{}}).bootstrap.rsc??={rsc:[]}).rsc.push(":HL[\"/_next/static/css/index.1ta3inz1.css\",\"style\" ]\n")</script><script>((self[Symbol.for("vinext.navigationRuntime")]??={bootstrap:{routeManifest:null},functions:{}}).bootstrap.rsc??={rsc:[]}).rsc.push("0:{\"__route\":\"route:/__vinext_nonexistent_for_404__\",\"__interceptionContext\":null,\"__layoutIds\":[],\"__rootLayout\":null,\"route:/__vinext_nonexistent_for_404__\":[[[\"$\",\"link\",\"css:/_next/static/css/index.1ta3inz1.css\",{\"rel\":\"stylesheet\",\"precedence\":\"vite-rsc/importer-resources\",\"href\":\"/_next/static/css/index.1ta3inz1.css\",\"data-rsc-css-href\":\"/_next/static/css/index.1ta3inz1.css\"}],\"$undefined\"],[\"$\",\"html\",null,{\"lang\":\"zh-CN\",\"children\":[\"$\",\"body\",null,{\"children\":[[\"$\",\"meta\",\"charset\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"robots\",{\"name\":\"robots\",\"content\":\"noindex\"}],[\"$\",\"$1\",\"metadata\",{\"children\":[[\"$\",\"title\",\"0\",{\"children\":\"豆格工坊|图片转拼豆图纸\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"在浏览器中把图片转换为拼豆像素图纸,按色号、名称或像素格筛选颜色。\"}]]}],[\"$\",\"$1\",\"viewport\",{\"children\":[[\"$\",\"meta\",\"0\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]}],[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]]]}]}]]}\n")</script><script>((self[Symbol.for("vinext.navigationRuntime")]??={bootstrap:{routeManifest:null},functions:{}}).bootstrap.rsc??={rsc:[]}).done=true</script></body></html>
<!DOCTYPE html><html lang="zh-CN"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="/_next/static/css/index.BINLXjqY.css" data-rsc-css-href="/_next/static/css/index.BINLXjqY.css" data-precedence="vite-rsc/importer-resources"/><link rel="modulepreload" fetchPriority="low" href="/_next/static/chunks/index-B9U9o3wW.js"/><script src="/_next/static/chunks/rolldown-runtime-C60lm6uB.js" type="module" async=""></script><script src="/_next/static/chunks/framework-BgSIrAUN.js" type="module" async=""></script><meta name="robots" content="noindex"/><title>豆格工坊|图片转拼豆图纸</title><meta name="description" content="在浏览器中把图片转换为拼豆像素图纸,按色号、名称或像素格筛选颜色。"/><title>404: This page could not be found.</title><script>Object.assign(((self[Symbol.for("vinext.navigationRuntime")]??={bootstrap:{routeManifest:null},functions:{}}).bootstrap.rsc??={rsc:[]}),{params:{},nav:{"pathname":"/__vinext_nonexistent_for_404__","searchParams":[]}})</script><link rel="modulepreload" href="/_next/static/chunks/index-B9U9o3wW.js" />
</head><body><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><script type="module" src="/_next/static/chunks/index-B9U9o3wW.js" id="_R_" async=""></script><script>((self[Symbol.for("vinext.navigationRuntime")]??={bootstrap:{routeManifest:null},functions:{}}).bootstrap.rsc??={rsc:[]}).rsc.push("1:\"$Sreact.fragment\"\n")</script><script>((self[Symbol.for("vinext.navigationRuntime")]??={bootstrap:{routeManifest:null},functions:{}}).bootstrap.rsc??={rsc:[]}).rsc.push(":HL[\"/_next/static/css/index.BINLXjqY.css\",\"style\" ]\n")</script><script>((self[Symbol.for("vinext.navigationRuntime")]??={bootstrap:{routeManifest:null},functions:{}}).bootstrap.rsc??={rsc:[]}).rsc.push("0:{\"__route\":\"route:/__vinext_nonexistent_for_404__\",\"__interceptionContext\":null,\"__layoutIds\":[],\"__rootLayout\":null,\"route:/__vinext_nonexistent_for_404__\":[[[\"$\",\"link\",\"css:/_next/static/css/index.BINLXjqY.css\",{\"rel\":\"stylesheet\",\"precedence\":\"vite-rsc/importer-resources\",\"href\":\"/_next/static/css/index.BINLXjqY.css\",\"data-rsc-css-href\":\"/_next/static/css/index.BINLXjqY.css\"}],\"$undefined\"],[\"$\",\"html\",null,{\"lang\":\"zh-CN\",\"children\":[\"$\",\"body\",null,{\"children\":[[\"$\",\"meta\",\"charset\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"robots\",{\"name\":\"robots\",\"content\":\"noindex\"}],[\"$\",\"$1\",\"metadata\",{\"children\":[[\"$\",\"title\",\"0\",{\"children\":\"豆格工坊|图片转拼豆图纸\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"在浏览器中把图片转换为拼豆像素图纸,按色号、名称或像素格筛选颜色。\"}]]}],[\"$\",\"$1\",\"viewport\",{\"children\":[[\"$\",\"meta\",\"0\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]}],[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]]]}]}]]}\n")</script><script>((self[Symbol.for("vinext.navigationRuntime")]??={bootstrap:{routeManifest:null},functions:{}}).bootstrap.rsc??={rsc:[]}).done=true</script></body></html>
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{r as e}from"./rolldown-runtime-C60lm6uB.js";import{r as t}from"./framework-BgSIrAUN.js";import{t as n}from"./index-BdxXUyhE.js";var r=e(t(),1),i=new Map;function a(e,t){return e?{...e,...t}:t}function o({providerId:e,segmentMap:t,children:o}){let s=(0,r.useRef)(null),c=n(),l=a(s.current??(e?i.get(e)??null:null),t);return(0,r.useEffect)(()=>{s.current=l,e&&i.set(e,l)},[l,e]),c?(0,r.createElement)(c.Provider,{value:l},o):o}export{o as LayoutSegmentProvider,a as mergeLayoutSegmentMap};
import{r as e}from"./rolldown-runtime-C60lm6uB.js";import{r as t}from"./framework-BgSIrAUN.js";import{t as n}from"./index-B9U9o3wW.js";var r=e(t(),1),i=new Map;function a(e,t){return e?{...e,...t}:t}function o({providerId:e,segmentMap:t,children:o}){let s=(0,r.useRef)(null),c=n(),l=a(s.current??(e?i.get(e)??null:null),t);return(0,r.useEffect)(()=>{s.current=l,e&&i.set(e,l)},[l,e]),c?(0,r.createElement)(c.Provider,{value:l},o):o}export{o as LayoutSegmentProvider,a as mergeLayoutSegmentMap};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -6,8 +6,8 @@
7:I["8c0f216c4604",[],"Slot",1]
8:I["9276801271d6",[],"AppRouterScrollTarget",1]
9:I["593f344dc510",[],"RedirectBoundary",1]
:HL["/_next/static/css/index.1ta3inz1.css","style" ]
0:{"__route":"route:/","__interceptionContext":null,"__layoutIds":["layout:/"],"__rootLayout":"/","__sourcePage":"/page","page:/":"$L1","layout:/":[[[["$","link","css:/_next/static/css/index.1ta3inz1.css",{"rel":"stylesheet","precedence":"vite-rsc/importer-resources","href":"/_next/static/css/index.1ta3inz1.css","data-rsc-css-href":"/_next/static/css/index.1ta3inz1.css"}],"$undefined"],["$","html",null,{"lang":"zh-CN","children":["$","body",null,{"children":["$","$L2",null,{}]}]}]],null],"route:/":[[["$","meta",null,{"charSet":"utf-8"}],[["$","title","0",{"children":"豆格工坊|图片转拼豆图纸"}],["$","meta","1",{"name":"description","content":"在浏览器中把图片转换为拼豆像素图纸,按色号、名称或像素格筛选颜色。"}]],[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]],["$","$L3",null,{"fallback":"$4","children":["$","$L5",null,{"fallback":"$4","children":["$","$L6",null,{"providerId":"layout:/","segmentMap":{"children":[]},"children":["$","$L7",null,{"id":"layout:/","parallelSlots":"$undefined","children":["$","$L8",null,{"children":["$","$L9",null,{"children":[["$","$L6",null,{"providerId":"page:/","segmentMap":{"children":["__PAGE__"]},"children":["$","$L7",null,{"id":"page:/"}]}],null]}]}]}]}]}]}],null,null],"__layoutFlags":{"layout:/":"s"},"__artifactCompatibility":{"schemaVersion":1,"graphVersion":"app-route-graph:1177fd80f83fa7b0","deploymentVersion":"c88aef44-f93f-48bd-8664-25c42b50397c","appElementsSchemaVersion":1,"rscPayloadSchemaVersion":1,"rootBoundaryId":"/","renderEpoch":null},"__renderObservation":{"schemaVersion":1,"output":{"kind":"app-rsc","mountedSlotsFingerprint":null,"renderEpoch":null,"rootBoundaryId":"/","routeId":"route:/"},"completeness":"partial","boundaryOutcome":{"kind":"unknown"},"requestApis":[{"kind":"connection","status":"unknown"},{"kind":"cookies","status":"unknown"},{"kind":"draftMode","status":"unknown"},{"kind":"headers","status":"unknown"},{"kind":"params","status":"unknown"},{"kind":"searchParams","status":"unknown"}],"dynamicFetches":[],"cacheTags":["/","_N_T_/","_N_T_/index","_N_T_/layout","_N_T_/page"],"pathTags":["/"],"cacheability":"unknown","downgrade":{"target":"freshRender","reasons":[{"code":"CP_DOWNGRADE_CACHEABILITY_UNKNOWN","target":"freshRender"},{"code":"CP_DOWNGRADE_INCOMPLETE_OBSERVATION","completeness":"partial","target":"freshRender"},{"code":"CP_DOWNGRADE_UNKNOWN_REQUEST_API","requestApi":"connection","target":"freshRender"},{"code":"CP_DOWNGRADE_UNKNOWN_REQUEST_API","requestApi":"cookies","target":"freshRender"},{"code":"CP_DOWNGRADE_UNKNOWN_REQUEST_API","requestApi":"draftMode","target":"freshRender"},{"code":"CP_DOWNGRADE_UNKNOWN_REQUEST_API","requestApi":"headers","target":"freshRender"},{"code":"CP_DOWNGRADE_UNKNOWN_REQUEST_API","requestApi":"params","target":"freshRender"},{"code":"CP_DOWNGRADE_UNKNOWN_REQUEST_API","requestApi":"searchParams","target":"freshRender"}],"fallback":{"kind":"breakerFallback","code":"CP_PRIVATE_DYNAMIC_DOWNGRADE","mode":"renderFresh","scope":"affectedOutput","fields":{"reasonCodes":["CP_DOWNGRADE_CACHEABILITY_UNKNOWN","CP_DOWNGRADE_INCOMPLETE_OBSERVATION","CP_DOWNGRADE_UNKNOWN_REQUEST_API","CP_DOWNGRADE_UNKNOWN_REQUEST_API","CP_DOWNGRADE_UNKNOWN_REQUEST_API","CP_DOWNGRADE_UNKNOWN_REQUEST_API","CP_DOWNGRADE_UNKNOWN_REQUEST_API","CP_DOWNGRADE_UNKNOWN_REQUEST_API"],"target":"freshRender"}},"isPublicCacheCandidate":false}}}
:HL["/_next/static/css/index.BINLXjqY.css","style" ]
0:{"__route":"route:/","__interceptionContext":null,"__layoutIds":["layout:/"],"__rootLayout":"/","__sourcePage":"/page","page:/":"$L1","layout:/":[[[["$","link","css:/_next/static/css/index.BINLXjqY.css",{"rel":"stylesheet","precedence":"vite-rsc/importer-resources","href":"/_next/static/css/index.BINLXjqY.css","data-rsc-css-href":"/_next/static/css/index.BINLXjqY.css"}],"$undefined"],["$","html",null,{"lang":"zh-CN","children":["$","body",null,{"children":["$","$L2",null,{}]}]}]],null],"route:/":[[["$","meta",null,{"charSet":"utf-8"}],[["$","title","0",{"children":"豆格工坊|图片转拼豆图纸"}],["$","meta","1",{"name":"description","content":"在浏览器中把图片转换为拼豆像素图纸,按色号、名称或像素格筛选颜色。"}]],[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]],["$","$L3",null,{"fallback":"$4","children":["$","$L5",null,{"fallback":"$4","children":["$","$L6",null,{"providerId":"layout:/","segmentMap":{"children":[]},"children":["$","$L7",null,{"id":"layout:/","parallelSlots":"$undefined","children":["$","$L8",null,{"children":["$","$L9",null,{"children":[["$","$L6",null,{"providerId":"page:/","segmentMap":{"children":["__PAGE__"]},"children":["$","$L7",null,{"id":"page:/"}]}],null]}]}]}]}]}]}],null,null],"__layoutFlags":{"layout:/":"s"},"__artifactCompatibility":{"schemaVersion":1,"graphVersion":"app-route-graph:1177fd80f83fa7b0","deploymentVersion":"1f311918-6c7f-4683-8b07-ca7bfb706323","appElementsSchemaVersion":1,"rscPayloadSchemaVersion":1,"rootBoundaryId":"/","renderEpoch":null},"__renderObservation":{"schemaVersion":1,"output":{"kind":"app-rsc","mountedSlotsFingerprint":null,"renderEpoch":null,"rootBoundaryId":"/","routeId":"route:/"},"completeness":"partial","boundaryOutcome":{"kind":"unknown"},"requestApis":[{"kind":"connection","status":"unknown"},{"kind":"cookies","status":"unknown"},{"kind":"draftMode","status":"unknown"},{"kind":"headers","status":"unknown"},{"kind":"params","status":"unknown"},{"kind":"searchParams","status":"unknown"}],"dynamicFetches":[],"cacheTags":["/","_N_T_/","_N_T_/index","_N_T_/layout","_N_T_/page"],"pathTags":["/"],"cacheability":"unknown","downgrade":{"target":"freshRender","reasons":[{"code":"CP_DOWNGRADE_CACHEABILITY_UNKNOWN","target":"freshRender"},{"code":"CP_DOWNGRADE_INCOMPLETE_OBSERVATION","completeness":"partial","target":"freshRender"},{"code":"CP_DOWNGRADE_UNKNOWN_REQUEST_API","requestApi":"connection","target":"freshRender"},{"code":"CP_DOWNGRADE_UNKNOWN_REQUEST_API","requestApi":"cookies","target":"freshRender"},{"code":"CP_DOWNGRADE_UNKNOWN_REQUEST_API","requestApi":"draftMode","target":"freshRender"},{"code":"CP_DOWNGRADE_UNKNOWN_REQUEST_API","requestApi":"headers","target":"freshRender"},{"code":"CP_DOWNGRADE_UNKNOWN_REQUEST_API","requestApi":"params","target":"freshRender"},{"code":"CP_DOWNGRADE_UNKNOWN_REQUEST_API","requestApi":"searchParams","target":"freshRender"}],"fallback":{"kind":"breakerFallback","code":"CP_PRIVATE_DYNAMIC_DOWNGRADE","mode":"renderFresh","scope":"affectedOutput","fields":{"reasonCodes":["CP_DOWNGRADE_CACHEABILITY_UNKNOWN","CP_DOWNGRADE_INCOMPLETE_OBSERVATION","CP_DOWNGRADE_UNKNOWN_REQUEST_API","CP_DOWNGRADE_UNKNOWN_REQUEST_API","CP_DOWNGRADE_UNKNOWN_REQUEST_API","CP_DOWNGRADE_UNKNOWN_REQUEST_API","CP_DOWNGRADE_UNKNOWN_REQUEST_API","CP_DOWNGRADE_UNKNOWN_REQUEST_API"],"target":"freshRender"}},"isPublicCacheCandidate":false}}}
a:I["6efdf509a785",[],"default",1]
1:["$","$La",null,{"params":"$@b","searchParams":"$@c"}]
b:{}
+1 -1
View File
@@ -1,3 +1,3 @@
{
"appBrowserEntry": "_next/static/chunks/index-BdxXUyhE.js"
"appBrowserEntry": "_next/static/chunks/index-B9U9o3wW.js"
}