Compare commits

...
21 Commits
Author SHA1 Message Date
wuyanwanwu ec68773179 Add China time to usage logs 2026-08-14 23:15:24 +08:00
wuyanwanwu d0a1a81ca2 Fix usage log file writes 2026-08-14 23:08:41 +08:00
wuyanwanwu 39abc86e6e Temporarily disable pattern import 2026-08-14 22:41:44 +08:00
wuyanwanwu 7d45dd8073 Improve high resolution PNG export 2026-08-14 22:37:35 +08:00
wuyanwanwu a65536e31e Add MARD pattern image import 2026-08-14 22:31:53 +08:00
wuyanwanwu 785a089518 Replace history color icon with pattern thumbnail 2026-08-14 21:56:14 +08:00
wuyanwanwu 6f101661c7 Add resizable mobile friendly crop selection 2026-08-14 21:52:40 +08:00
wuyanwanwu 365af027b8 Add coordinates and color legend to PNG export 2026-08-14 21:38:37 +08:00
wuyanwanwu 4f8d46a523 Add mouse wheel zoom to pattern preview 2026-08-14 02:10:34 +08:00
wuyanwanwu d53f8f4585 Replace crop zoom with image selection dialog 2026-08-14 02:04:36 +08:00
wuyanwanwu e028ecc9c8 Refine gradients and remove isolated colors 2026-08-14 01:57:19 +08:00
wuyanwanwu 02a17f5283 Preserve broad gradients without edge artifacts 2026-08-14 01:41:38 +08:00
wuyanwanwu 1de77c904e Fix crop range preview zoom and history saving 2026-08-14 01:37:19 +08:00
wuyanwanwu cf0f53d148 Restore smooth gradient sampling 2026-08-14 01:33:23 +08:00
wuyanwanwu daffe4daca Improve transition edge cleanup 2026-08-14 01:29:04 +08:00
wuyanwanwu 8c505a205c Reduce mixed colors in dominant sampling 2026-08-14 01:23:37 +08:00
wuyanwanwu 1bd885f5b0 Use center weighted sampling and fix pattern border 2026-08-14 01:18:23 +08:00
wuyanwanwu a5d4850728 Fix history storage and default conversion options 2026-08-14 01:13:23 +08:00
wuyanwanwu 552ed5e6fa Expand preview and center dominant sampling 2026-08-14 01:11:38 +08:00
wuyanwanwu 5a312f5039 Add persistent anonymous usage logging 2026-08-14 01:05:50 +08:00
wuyanwanwu b890ce4035 Protect object corners during color cleanup 2026-08-14 00:54:45 +08:00
19 changed files with 1532 additions and 226 deletions
+1
View File
@@ -40,3 +40,4 @@ yarn-error.log*
/outputs/
/work/
/mard-source.html
/data/
+84 -14
View File
@@ -48,13 +48,14 @@ button { color: inherit; }
.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 .file-input { display: 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; }
.crop-preview { cursor: default; }
.upload-overlay { position: absolute; z-index: 3; right: 10px; bottom: 10px; padding: 7px 10px; border: 0; color: white; background: rgba(22, 75, 57, .9); cursor: pointer; 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; }
.crop-select-button { width: 100%; margin-top: 0; padding: 10px; border: 1px solid var(--ink); background: var(--lime); cursor: pointer; font-size: 12px; font-weight: 700; }
.reset-crop { width: 100%; margin: 7px 0 0; padding: 7px; border: 1px solid var(--line); background: white; cursor: pointer; color: var(--muted); font-size: 11px; }
.pattern-import-button { width: 100%; margin: 7px 0 20px; padding: 9px; border: 1px solid #b6532d; background: #fff5ec; color: #8f3f20; cursor: pointer; font-size: 12px; font-weight: 700; }
.pattern-import-button:hover { background: #fee5d1; }
.pattern-import-file { display: none; }
.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; }
@@ -76,10 +77,10 @@ input[type="range"] { accent-color: var(--green); }
.zoom-control button { width: 36px; height: 36px; border: 1px solid var(--line); background: white; cursor: pointer; font-size: 20px; }
.zoom-control input { width: 95px; }
.zoom-control output { width: 38px; color: var(--muted); font-size: 11px; text-align: right; }
.canvas-viewport { position: relative; width: 100%; height: clamp(520px, 68vh, 760px); min-height: 0; flex: 0 0 clamp(520px, 68vh, 760px); contain: size layout; overflow: hidden; }
.canvas-stage { position: absolute; inset: 0; width: auto; height: auto; overflow: scroll; display: block; padding: 36px; cursor: grab; touch-action: none; overscroll-behavior: contain; user-select: none; scrollbar-gutter: stable; }
.canvas-viewport { position: relative; width: 100%; aspect-ratio: 1 / 1; height: auto; min-height: 0; flex: 0 0 auto; align-self: stretch; contain: size layout; overflow: hidden; }
.canvas-stage { position: absolute; inset: 0; width: auto; height: auto; overflow: scroll; display: block; padding: 12px; cursor: grab; touch-action: none; overscroll-behavior: contain; user-select: none; scrollbar-gutter: stable; }
.canvas-stage.is-dragging { cursor: grabbing; }
.canvas-wrap { width: max-content; line-height: 0; position: relative; margin: auto; border: 1px solid var(--ink); box-shadow: 10px 10px 0 rgba(32,39,36,.13); background: white; }
.canvas-wrap { width: max-content; line-height: 0; position: relative; margin: auto; border: 1px solid var(--ink); background-color: #f7f5ef; background-image: linear-gradient(45deg, #dedbd3 25%, transparent 25%), linear-gradient(-45deg, #dedbd3 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #dedbd3 75%), linear-gradient(-45deg, transparent 75%, #dedbd3 75%); background-position: 0 0, 0 5px, 5px -5px, -5px 0; background-size: 10px 10px; }
.canvas-wrap canvas { cursor: inherit; image-rendering: pixelated; }
.pixel-tooltip { position: absolute; z-index: 5; width: max-content; max-width: 260px; padding: 9px 11px; display: grid; grid-template-columns: 14px auto; align-items: center; gap: 3px 8px; color: white; background: rgba(22, 30, 27, .94); border: 1px solid rgba(255,255,255,.2); box-shadow: 4px 4px 0 rgba(0,0,0,.2); border-radius: 3px; line-height: 1.35; pointer-events: none; }
.tooltip-swatch { width: 14px; height: 14px; grid-row: 1 / 3; border-radius: 50%; border: 1px solid rgba(255,255,255,.6); }
@@ -130,9 +131,9 @@ input[type="range"] { accent-color: var(--green); }
.history-heading p:not(.section-kicker) { margin: 8px 0 0; color: var(--muted); font-size: 12px; }
.history-heading .search-box { width: min(330px, 100%); }
.history-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; padding-top: 18px; }
.history-card { min-width: 0; padding: 16px; display: grid; grid-template-columns: 58px minmax(0,1fr); gap: 12px; border: 1px solid var(--line); background: var(--card); }
.history-swatches { width: 58px; height: 58px; display: flex; overflow: hidden; border: 1px solid var(--ink); }
.history-swatches i { min-width: 3px; }
.history-card { min-width: 0; padding: 16px; display: grid; grid-template-columns: 86px minmax(0,1fr); gap: 13px; border: 1px solid var(--line); background: var(--card); }
.history-thumbnail { width: 86px; height: 86px; display: grid; place-items: center; overflow: hidden; border: 1px solid var(--ink); background-color: #f8f6f0; background-image: linear-gradient(45deg, #dedbd3 25%, transparent 25%), linear-gradient(-45deg, #dedbd3 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #dedbd3 75%), linear-gradient(-45deg, transparent 75%, #dedbd3 75%); background-position: 0 0, 0 5px, 5px -5px, -5px 0; background-size: 10px 10px; }
.history-thumbnail canvas { display: block; width: auto; height: auto; max-width: 100%; max-height: 100%; image-rendering: pixelated; }
.history-card h3 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin: 3px 0 7px; font-size: 14px; }
.history-card p { margin: 0; color: var(--muted); font-size: 10px; }
.history-actions { grid-column: 1 / -1; display: flex; gap: 7px; justify-content: flex-end; }
@@ -140,6 +141,55 @@ input[type="range"] { accent-color: var(--green); }
.history-actions button:first-child { background: var(--lime); font-weight: 700; }
.history-empty { margin-top: 18px; padding: 44px 20px; color: var(--muted); text-align: center; border: 1px dashed var(--line); background: rgba(255,253,248,.5); font-size: 13px; }
.crop-dialog-backdrop { position: fixed; inset: 0; z-index: 100; display: grid; place-items: center; padding: 24px; background: rgba(17,24,21,.76); backdrop-filter: blur(5px); }
.crop-dialog { width: min(1280px, 96vw); max-height: 94vh; display: flex; flex-direction: column; overflow: hidden; border: 1px solid var(--ink); background: var(--card); box-shadow: 10px 10px 0 rgba(0,0,0,.32); }
.crop-dialog-header { flex: 0 0 auto; padding: 18px 22px; display: flex; align-items: flex-start; justify-content: space-between; gap: 24px; border-bottom: 1px solid var(--line); }
.crop-dialog-header .section-kicker { margin-bottom: 5px; }
.crop-dialog-header h2 { margin: 0; font-size: 22px; }
.crop-dialog-header p:not(.section-kicker) { margin: 6px 0 0; color: var(--muted); font-size: 12px; }
.crop-dialog-header > button { width: 38px; height: 38px; flex: 0 0 auto; border: 1px solid var(--line); background: white; cursor: pointer; font-size: 25px; line-height: 1; }
.crop-mode-bar { min-height: 54px; padding: 8px 20px; display: flex; align-items: center; gap: 12px; border-bottom: 1px solid var(--line); background: #f7f4ec; color: var(--muted); font-size: 11px; }
.crop-mode-switch { display: inline-flex; border: 1px solid var(--ink); background: white; }
.crop-mode-switch button { min-height: 34px; padding: 0 15px; border: 0; border-right: 1px solid var(--ink); background: transparent; cursor: pointer; font-size: 12px; }
.crop-mode-switch button:last-child { border-right: 0; }
.crop-mode-switch button.active { background: var(--green); color: white; font-weight: 700; }
.pattern-import-dimensions { display: flex; align-items: flex-end; gap: 8px; }
.pattern-import-dimensions label { display: grid; gap: 3px; color: var(--muted); font-size: 10px; }
.pattern-import-dimensions input { width: 82px; height: 34px; padding: 0 8px; border: 1px solid var(--line); background: white; color: var(--ink); font-size: 12px; }
.pattern-import-dimensions b { padding-bottom: 9px; color: var(--muted); font-weight: 400; }
.pattern-import-empty-option { display: flex; align-items: center; gap: 6px; color: var(--ink); cursor: pointer; }
.pattern-import-empty-option input { accent-color: var(--green); }
.crop-size-readout { margin-left: auto; font-variant-numeric: tabular-nums; }
.crop-dialog-scroll { min-height: 0; flex: 1 1 auto; overflow: auto; padding: 18px; display: grid; place-items: center; background-color: #e9e5db; background-image: radial-gradient(#c8c3b8 .7px, transparent .7px); background-size: 12px 12px; }
.crop-dialog-stage { position: relative; width: max-content; max-width: 100%; line-height: 0; cursor: crosshair; touch-action: none; user-select: none; box-shadow: 0 0 0 1px var(--ink); }
.crop-dialog-stage.can-move-selection { cursor: move; }
.crop-dialog-stage.is-moving-selection { cursor: grabbing; }
.crop-dialog-stage.is-resizing-selection { cursor: nwse-resize; }
.crop-dialog-stage canvas { display: block; max-width: 100%; height: auto; pointer-events: none; }
.crop-selection-mask { position: absolute; z-index: 1; background: rgba(10,15,13,.55); pointer-events: none; }
.crop-selection-top { inset: 0 0 auto; }
.crop-selection-left { left: 0; }
.crop-selection-bottom { inset: auto 0 0; bottom: 0; }
.crop-selection-box { position: absolute; z-index: 2; border: 2px solid white; outline: 1px solid rgba(0,0,0,.75); box-shadow: inset 0 0 0 1px rgba(0,0,0,.18); pointer-events: none; }
.crop-selection-box::before, .crop-selection-box::after { content: ""; position: absolute; background: rgba(255,255,255,.62); }
.crop-selection-box::before { left: 33.333%; top: 0; bottom: 0; width: 1px; box-shadow: calc(33.333% * 1) 0 0 rgba(255,255,255,.62); }
.crop-selection-box::after { left: 0; right: 0; top: 33.333%; height: 1px; box-shadow: 0 calc(33.333% * 1) 0 rgba(255,255,255,.62); }
.pattern-import-grid-overlay { position: absolute; z-index: 3; inset: 0; display: block; pointer-events: none; background-image: linear-gradient(to right, rgba(255,102,0,.66) 1px, transparent 1px), linear-gradient(to bottom, rgba(255,102,0,.66) 1px, transparent 1px); box-shadow: inset -1px -1px 0 rgba(255,102,0,.66); }
.crop-resize-handle { position: absolute; z-index: 4; width: 15px; height: 15px; display: block; border: 2px solid white; border-radius: 50%; background: var(--green); box-shadow: 0 1px 4px rgba(0,0,0,.48); pointer-events: auto; }
.handle-nw { left: 0; top: 0; transform: translate(-50%, -50%); cursor: nwse-resize; }
.handle-ne { right: 0; top: 0; transform: translate(50%, -50%); cursor: nesw-resize; }
.handle-sw { left: 0; bottom: 0; transform: translate(-50%, 50%); cursor: nesw-resize; }
.handle-se { right: 0; bottom: 0; transform: translate(50%, 50%); cursor: nwse-resize; }
.handle-n { left: 50%; top: 0; transform: translate(-50%, -50%); cursor: ns-resize; }
.handle-s { left: 50%; bottom: 0; transform: translate(-50%, 50%); cursor: ns-resize; }
.handle-w { left: 0; top: 50%; transform: translate(-50%, -50%); cursor: ew-resize; }
.handle-e { right: 0; top: 50%; transform: translate(50%, -50%); cursor: ew-resize; }
.crop-dialog-actions { flex: 0 0 auto; min-height: 66px; padding: 12px 20px; display: flex; align-items: center; justify-content: flex-end; gap: 10px; border-top: 1px solid var(--line); }
.crop-dialog-actions span { margin-right: auto; color: var(--muted); font-size: 11px; }
.crop-dialog-actions button { min-height: 40px; padding: 0 16px; border: 1px solid var(--ink); background: white; cursor: pointer; font-size: 12px; }
.crop-dialog-actions .confirm { background: var(--green); color: white; font-weight: 700; }
.crop-dialog-actions .confirm:disabled { opacity: .42; cursor: not-allowed; }
@media (max-width: 1160px) {
.workspace { grid-template-columns: 260px minmax(500px, 1fr); }
.color-panel { grid-column: 1 / -1; border-left: 0; border-top: 1px solid var(--ink); max-height: none; }
@@ -161,8 +211,8 @@ input[type="range"] { accent-color: var(--green); }
.pattern-toolbar { align-items: flex-start; flex-direction: column; padding: 20px; }
.zoom-control { width: 100%; }
.zoom-control input { flex: 1; }
.canvas-viewport { height: max(360px, min(68vh, 620px)); min-height: 0; flex-basis: max(360px, min(68vh, 620px)); }
.canvas-stage { display: block; padding: 24px; }
.canvas-viewport { width: 100%; height: auto; min-height: 0; aspect-ratio: 1 / 1; flex-basis: auto; }
.canvas-stage { display: block; padding: 10px; }
.canvas-wrap { margin: auto; }
.pattern-footer { align-items: flex-start; flex-wrap: wrap; padding: 14px 18px; }
.view-options { width: 100%; margin: 5px 0 0; justify-content: space-between; }
@@ -175,6 +225,26 @@ input[type="range"] { accent-color: var(--green); }
.history-section { margin: 52px auto; }
.history-heading .search-box { width: 100%; }
.history-grid { grid-template-columns: 1fr; }
.crop-dialog-backdrop { padding: 0; }
.crop-dialog { width: 100vw; max-height: 100vh; height: 100vh; border: 0; box-shadow: none; }
.crop-dialog-header { padding: 14px 16px; }
.crop-dialog-header h2 { font-size: 18px; }
.crop-mode-bar { padding: 8px 12px; flex-wrap: wrap; gap: 7px 10px; }
.crop-mode-switch { flex: 1; }
.crop-mode-switch button { flex: 1; padding: 0 8px; }
.pattern-import-dimensions { width: 100%; }
.pattern-import-dimensions label { flex: 1; }
.pattern-import-dimensions input { width: 100%; min-height: 42px; }
.pattern-import-empty-option { width: 100%; min-height: 34px; }
.crop-size-readout { width: 100%; margin-left: 0; }
.crop-resize-handle { width: 28px; height: 28px; border-width: 3px; }
.crop-edge-handle { display: none; }
.crop-dialog-scroll { padding: 10px; }
.crop-dialog-stage { width: 100%; max-width: 100%; }
.crop-dialog-stage canvas { width: 100%; max-width: 100%; height: auto; }
.crop-dialog-actions { padding: 10px 12px; flex-wrap: wrap; }
.crop-dialog-actions span { width: 100%; order: -1; }
.crop-dialog-actions button { flex: 1; padding: 0 10px; }
input, select, button { font-size: 16px; }
.search-box input, .color-item, .selection-tools button { font-size: 12px; }
}
@@ -182,7 +252,7 @@ input[type="range"] { accent-color: var(--green); }
@media (max-width: 370px) {
.view-options { gap: 6px; }
.view-options label { font-size: 10px; }
.canvas-stage { padding: 16px; }
.canvas-stage { padding: 8px; }
}
@media (prefers-reduced-motion: reduce) { * { scroll-behavior: auto !important; transition: none !important; } }
+1323 -115
View File
File diff suppressed because it is too large Load Diff
+66 -1
View File
@@ -1,10 +1,22 @@
import { createReadStream, existsSync, statSync } from "node:fs";
import { createReadStream, existsSync, mkdirSync, statSync } from "node:fs";
import { appendFile } from "node:fs/promises";
import { createHash } from "node:crypto";
import { createServer } from "node:http";
import { extname, join, normalize } from "node:path";
const packagedRoot = join(process.cwd(), "public");
const root = existsSync(join(packagedRoot, "index.html")) ? packagedRoot : join(process.cwd(), "out");
const port = Number(process.env.APP_PORT || 3200);
const dataDirectory = process.env.DATA_DIR || join(process.cwd(), "data");
const usageLogPath = join(dataDirectory, "usage.jsonl");
mkdirSync(dataDirectory, { recursive: true });
function formatChinaTimestamp(date) {
return new Date(date.getTime() + 8 * 60 * 60 * 1000)
.toISOString()
.replace("Z", "+08:00");
}
const mimeTypes = {
".css": "text/css; charset=utf-8",
".html": "text/html; charset=utf-8",
@@ -17,8 +29,61 @@ const mimeTypes = {
".webp": "image/webp",
};
function sendJson(response, status, value) {
response.writeHead(status, { "Content-Type": "application/json; charset=utf-8", "Cache-Control": "no-store" });
response.end(JSON.stringify(value));
}
function writeUsageLog(request, response) {
let body = "";
request.setEncoding("utf8");
request.on("data", (chunk) => {
body += chunk;
if (body.length > 16_384) request.destroy();
});
request.on("end", async () => {
try {
const input = JSON.parse(body || "{}");
if (!['page_view', 'conversion'].includes(input.event)) {
sendJson(response, 400, { error: "Invalid event" });
return;
}
const now = new Date();
const entry = {
timestamp: now.toISOString(),
timestamp_cn: formatChinaTimestamp(now),
event: input.event,
client_hash: request.headers["x-forwarded-for"] || request.socket.remoteAddress
? createHash("sha256").update(String(request.headers["x-forwarded-for"] || request.socket.remoteAddress)).digest("hex").slice(0, 16)
: undefined,
...(input.event === "conversion" ? {
width: Math.max(8, Math.min(256, Number(input.width) || 0)),
height: Math.max(8, Math.min(256, Number(input.height) || 0)),
sampling_strategy: input.sampling_strategy === "dominant" ? "dominant" : "smooth",
color_limit: Math.max(2, Math.min(64, Number(input.color_limit) || 0)),
actual_colors: Math.max(0, Math.min(221, Number(input.actual_colors) || 0)),
bead_count: Math.max(0, Math.min(65_536, Number(input.bead_count) || 0)),
transparent_cells: Math.max(0, Math.min(65_536, Number(input.transparent_cells) || 0)),
} : {}),
};
await appendFile(usageLogPath, `${JSON.stringify(entry)}\n`, "utf8");
sendJson(response, 202, { ok: true });
} catch {
sendJson(response, 400, { error: "Invalid request" });
}
});
}
createServer((request, response) => {
const pathname = decodeURIComponent(new URL(request.url || "/", "http://localhost").pathname);
if (pathname === "/api/usage" && request.method === "POST") {
writeUsageLog(request, response);
return;
}
if (pathname.startsWith("/api/")) {
sendJson(response, 404, { error: "Not found" });
return;
}
const relativePath = normalize(pathname).replace(/^([/\\])+/, "");
let filePath = join(root, relativePath || "index.html");
+2
View File
@@ -4,3 +4,5 @@ services:
restart: unless-stopped
ports:
- "${APP_PORT:-3200}:3200"
volumes:
- ./data:/app/data
+3 -3
View File
@@ -11,7 +11,7 @@
"name": "rolldown-runtime"
},
"app/page.tsx": {
"file": "_next/static/chunks/page-DoVw8RjR.js",
"file": "_next/static/chunks/page-DBRuBfy6.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-BkDyc3uQ.js",
"file": "_next/static/chunks/layout-segment-context-rAphBSzO.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-_0qXxkjI.js",
"file": "_next/static/chunks/index-VFVBI02U.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.DWpM5cM2.css" data-rsc-css-href="/_next/static/css/index.DWpM5cM2.css" data-precedence="vite-rsc/importer-resources"/><link rel="modulepreload" fetchPriority="low" href="/_next/static/chunks/index-_0qXxkjI.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-_0qXxkjI.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-_0qXxkjI.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.DWpM5cM2.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.DWpM5cM2.css\",{\"rel\":\"stylesheet\",\"precedence\":\"vite-rsc/importer-resources\",\"href\":\"/_next/static/css/index.DWpM5cM2.css\",\"data-rsc-css-href\":\"/_next/static/css/index.DWpM5cM2.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.B4qFKCfT.css" data-rsc-css-href="/_next/static/css/index.B4qFKCfT.css" data-precedence="vite-rsc/importer-resources"/><link rel="modulepreload" fetchPriority="low" href="/_next/static/chunks/index-VFVBI02U.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-VFVBI02U.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-VFVBI02U.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.B4qFKCfT.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.B4qFKCfT.css\",{\"rel\":\"stylesheet\",\"precedence\":\"vite-rsc/importer-resources\",\"href\":\"/_next/static/css/index.B4qFKCfT.css\",\"data-rsc-css-href\":\"/_next/static/css/index.B4qFKCfT.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-_0qXxkjI.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-VFVBI02U.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.DWpM5cM2.css","style" ]
0:{"__route":"route:/","__interceptionContext":null,"__layoutIds":["layout:/"],"__rootLayout":"/","__sourcePage":"/page","page:/":"$L1","layout:/":[[[["$","link","css:/_next/static/css/index.DWpM5cM2.css",{"rel":"stylesheet","precedence":"vite-rsc/importer-resources","href":"/_next/static/css/index.DWpM5cM2.css","data-rsc-css-href":"/_next/static/css/index.DWpM5cM2.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":"bd7f7592-0673-40a8-affb-375abb01af88","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.B4qFKCfT.css","style" ]
0:{"__route":"route:/","__interceptionContext":null,"__layoutIds":["layout:/"],"__rootLayout":"/","__sourcePage":"/page","page:/":"$L1","layout:/":[[[["$","link","css:/_next/static/css/index.B4qFKCfT.css",{"rel":"stylesheet","precedence":"vite-rsc/importer-resources","href":"/_next/static/css/index.B4qFKCfT.css","data-rsc-css-href":"/_next/static/css/index.B4qFKCfT.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":"feb6bb1f-6465-425f-8535-1fdc32d89efe","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-_0qXxkjI.js"
"appBrowserEntry": "_next/static/chunks/index-VFVBI02U.js"
}
+28 -68
View File
@@ -1,91 +1,51 @@
import assert from "node:assert/strict";
import { access, readFile, readdir } from "node:fs/promises";
import { readFile } from "node:fs/promises";
import test from "node:test";
const developmentPreviewMeta =
/<meta(?=[^>]*\bname=["']codex-preview["'])(?=[^>]*\bcontent=["']development["'])[^>]*>/i;
const templateRoot = new URL("../", import.meta.url);
const previewRoot = new URL("../app/_sites-preview/", import.meta.url);
async function render() {
const workerUrl = new URL("../dist/server/index.js", import.meta.url);
workerUrl.searchParams.set("test", `${process.pid}-${Date.now()}`);
const { default: worker } = await import(workerUrl.href);
return worker.fetch(
new Request("http://localhost/", {
headers: { accept: "text/html" },
}),
{
ASSETS: {
fetch: async () => new Response("Not found", { status: 404 }),
},
},
{
waitUntil() {},
passThroughOnException() {},
},
new Request("http://localhost/", { headers: { accept: "text/html" } }),
{ ASSETS: { fetch: async () => new Response("Not found", { status: 404 }) } },
{ waitUntil() {}, passThroughOnException() {} },
);
}
test("server-renders the starter loading skeleton", async () => {
test("server-renders the pixel bead application shell", async () => {
const response = await render();
assert.equal(response.status, 200);
assert.match(response.headers.get("content-type") ?? "", /^text\/html\b/i);
const html = await response.text();
assert.match(html, developmentPreviewMeta);
assert.match(html, /<title>Your site is taking shape<\/title>/i);
assert.match(html, /Building your site/);
assert.match(html, /Your site is taking shape/);
assert.match(
html,
/Your first version will appear here automatically when its ready\./,
);
assert.doesNotMatch(html, /Codex/);
assert.match(html, /react-loading-skeleton/);
assert.match(html, /role="status"/);
assert.match(html, /<title>豆格工坊|图片转拼豆图纸<\/title>/i);
assert.doesNotMatch(html, /导入现成图纸/);
assert.match(html, /MARD 221 基础色卡/);
assert.match(html, /图片只在当前浏览器中处理/);
assert.doesNotMatch(html, /codex-preview|Your site is taking shape/i);
});
test("keeps the loading skeleton scoped and disposable", async () => {
const [preview, css, page, layout, packageJson, files] = await Promise.all([
readFile(new URL("SkeletonPreview.tsx", previewRoot), "utf8"),
readFile(new URL("preview.css", previewRoot), "utf8"),
test("keeps MARD pattern import wired to the crop and recognition flow", async () => {
const [page, css, palette] = await Promise.all([
readFile(new URL("../app/page.tsx", import.meta.url), "utf8"),
readFile(new URL("../app/layout.tsx", import.meta.url), "utf8"),
readFile(new URL("../package.json", import.meta.url), "utf8"),
readdir(previewRoot),
readFile(new URL("../app/globals.css", import.meta.url), "utf8"),
readFile(new URL("../app/mard-palette.ts", import.meta.url), "utf8"),
]);
assert.deepEqual(files.sort(), ["SkeletonPreview.tsx", "preview.css"]);
assert.match(preview, /from "react-loading-skeleton"/);
assert.match(preview, /baseColor="#eceae7"/);
assert.match(preview, /highlightColor="#f9f8f6"/);
assert.match(preview, /duration=\{2\.8\}/);
assert.match(preview, /sites-skeleton-search-placeholder/);
assert.match(packageJson, /"react-loading-skeleton": "3\.5\.0"/);
const shellIndex = preview.indexOf('className="sites-skeleton-shell"');
const statusIndex = preview.indexOf('className="sites-skeleton-status"');
assert.ok(shellIndex >= 0 && statusIndex > shellIndex);
assert.match(css, /position:\s*fixed/);
assert.match(css, /inset:\s*0/);
assert.match(css, /opacity:\s*0\.52/);
assert.match(css, /prefers-reduced-motion:\s*reduce/);
assert.doesNotMatch(css, /#020617|canvas|pets|progress/i);
assert.doesNotMatch(
preview,
/loading-spinner|status-mark|status-progress|canvas|cookie|random/i,
);
assert.match(page, /export const metadata:\s*Metadata/);
assert.match(page, /"codex-preview": "development"/);
assert.match(page, /<SkeletonPreview \/>/);
assert.match(layout, /title:\s*"Starter Project"/);
assert.doesNotMatch(layout, /codex-preview|_sites-preview|themeColor|\bViewport\b/);
assert.doesNotMatch(css, /(^|\s)(html|body)\s*\{/m);
await assert.rejects(
access(new URL("public/_sites-preview", templateRoot)),
);
assert.match(page, /function importMardPatternImage\(/);
assert.match(page, /const ENABLE_PATTERN_IMPORT = false/);
assert.match(page, /recognizedCodes/);
assert.match(page, /missingCodeAsEmpty/);
assert.match(page, /没有识别到 MARD 编码的格子视为空白/);
assert.match(page, /pattern-import-grid-overlay/);
assert.match(page, /max="256"/);
assert.match(page, /maximumExportSide = 8192/);
assert.match(page, /color\.code.*color\.count.*颗/);
assert.match(page, /下载高清 PNG/);
assert.match(css, /\.pattern-import-grid-overlay/);
assert.match(css, /@media \(max-width: 760px\)/);
assert.match(palette, /export const MARD_221/);
assert.match(palette, /A:\s*"#[0-9A-F]{6}/);
});