Add persistent anonymous usage logging
This commit is contained in:
@@ -40,3 +40,4 @@ yarn-error.log*
|
||||
/outputs/
|
||||
/work/
|
||||
/mard-source.html
|
||||
/data/
|
||||
|
||||
+2
-2
@@ -76,7 +76,7 @@ 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-viewport { position: relative; width: min(100%, 82vh, 1000px); aspect-ratio: 1 / 1; height: auto; min-height: 0; flex: 0 0 auto; align-self: center; 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-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; }
|
||||
@@ -161,7 +161,7 @@ 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-viewport { width: min(100%, 82vh); height: auto; min-height: 0; aspect-ratio: 1 / 1; flex-basis: auto; }
|
||||
.canvas-stage { display: block; padding: 24px; }
|
||||
.canvas-wrap { margin: auto; }
|
||||
.pattern-footer { align-items: flex-start; flex-wrap: wrap; padding: 14px 18px; }
|
||||
|
||||
+91
-28
@@ -28,8 +28,17 @@ type ColorCluster = {
|
||||
|
||||
type SamplingStrategy = "smooth" | "dominant";
|
||||
|
||||
function logUsage(event: Record<string, unknown>) {
|
||||
void fetch("/api/usage", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(event),
|
||||
keepalive: true,
|
||||
}).catch(() => undefined);
|
||||
}
|
||||
|
||||
type Pixel = {
|
||||
color: BeadColor;
|
||||
color: BeadColor | null;
|
||||
};
|
||||
|
||||
type HoveredPixel = {
|
||||
@@ -47,9 +56,11 @@ type SavedPattern = {
|
||||
width: number;
|
||||
height: number;
|
||||
colorLimit: number;
|
||||
codes: string[];
|
||||
codes: Array<string | null>;
|
||||
};
|
||||
|
||||
const TRANSPARENT_ALPHA_THRESHOLD = 128;
|
||||
|
||||
const MARD_SERIES_NAMES: Record<string, string> = {
|
||||
A: "黄橙系", B: "绿色系", C: "蓝青系", D: "紫蓝系", E: "粉红系",
|
||||
F: "红色系", G: "肤棕系", H: "黑白灰系", M: "莫兰迪系",
|
||||
@@ -102,9 +113,13 @@ function nearestColor(lab: Oklab, palette: BeadColor[]) {
|
||||
}
|
||||
|
||||
function mergePerceptualColors(data: Uint8ClampedArray) {
|
||||
const pixelKeys: number[] = [];
|
||||
const pixelKeys: Array<number | null> = [];
|
||||
const histogram = new Map<number, { count: number; red: number; green: number; blue: number }>();
|
||||
for (let index = 0; index < data.length; index += 4) {
|
||||
if (data[index + 3] < TRANSPARENT_ALPHA_THRESHOLD) {
|
||||
pixelKeys.push(null);
|
||||
continue;
|
||||
}
|
||||
const red = data[index];
|
||||
const green = data[index + 1];
|
||||
const blue = data[index + 2];
|
||||
@@ -187,7 +202,7 @@ function chooseDistinctMardColors(clusters: ColorCluster[], maximum: number) {
|
||||
separated.push(candidate.color);
|
||||
}
|
||||
}
|
||||
return separated.slice(0, Math.max(1, Math.min(maximum, separated.length)));
|
||||
return separated.slice(0, Math.max(0, Math.min(maximum, separated.length)));
|
||||
}
|
||||
|
||||
function sampleDominantRegions(
|
||||
@@ -204,8 +219,7 @@ function sampleDominantRegions(
|
||||
sample.width = width * scale;
|
||||
sample.height = height * scale;
|
||||
const ctx = sample.getContext("2d", { willReadFrequently: true })!;
|
||||
ctx.fillStyle = "#f7f5ed";
|
||||
ctx.fillRect(0, 0, sample.width, sample.height);
|
||||
ctx.clearRect(0, 0, sample.width, sample.height);
|
||||
drawFittedImage(ctx, image, sample.width, sample.height, fitMode, cropZoom, cropX, cropY);
|
||||
const source = ctx.getImageData(0, 0, sample.width, sample.height).data;
|
||||
const result = new Uint8ClampedArray(width * height * 4);
|
||||
@@ -215,9 +229,14 @@ function sampleDominantRegions(
|
||||
for (let row = 0; row < height; row++) {
|
||||
for (let column = 0; column < width; column++) {
|
||||
const groups: Array<{ count: number; lab: Oklab; samples: Array<{ rgb: [number, number, number]; lab: Oklab }> }> = [];
|
||||
let transparentCount = 0;
|
||||
for (let offsetY = 0; offsetY < scale; offsetY++) {
|
||||
for (let offsetX = 0; offsetX < scale; offsetX++) {
|
||||
const sourceIndex = ((row * scale + offsetY) * sample.width + column * scale + offsetX) * 4;
|
||||
if (source[sourceIndex + 3] < TRANSPARENT_ALPHA_THRESHOLD) {
|
||||
transparentCount += 1;
|
||||
continue;
|
||||
}
|
||||
const rgb: [number, number, number] = [source[sourceIndex], source[sourceIndex + 1], source[sourceIndex + 2]];
|
||||
const lab = rgbToOklab(rgb);
|
||||
let closest: (typeof groups)[number] | undefined;
|
||||
@@ -239,23 +258,28 @@ function sampleDominantRegions(
|
||||
}
|
||||
}
|
||||
}
|
||||
const targetIndex = (row * width + column) * 4;
|
||||
if (transparentCount >= scale * scale / 2 || groups.length === 0) {
|
||||
result[targetIndex + 3] = 0;
|
||||
confidence[row * width + column] = transparentCount / (scale * scale);
|
||||
continue;
|
||||
}
|
||||
const dominant = groups.sort((a, b) => b.count - a.count)[0];
|
||||
const representative = dominant.samples.reduce((best, current) =>
|
||||
oklabDistance(current.lab, dominant.lab) < oklabDistance(best.lab, dominant.lab) ? current : best,
|
||||
);
|
||||
const targetIndex = (row * width + column) * 4;
|
||||
result[targetIndex] = representative.rgb[0];
|
||||
result[targetIndex + 1] = representative.rgb[1];
|
||||
result[targetIndex + 2] = representative.rgb[2];
|
||||
result[targetIndex + 3] = 255;
|
||||
confidence[row * width + column] = dominant.count / (scale * scale);
|
||||
confidence[row * width + column] = dominant.count / Math.max(1, scale * scale - transparentCount);
|
||||
}
|
||||
}
|
||||
return { data: result, confidence };
|
||||
}
|
||||
|
||||
function cleanLowConfidenceIsolatedColors(
|
||||
colors: BeadColor[],
|
||||
colors: Array<BeadColor | null>,
|
||||
confidence: Float32Array,
|
||||
width: number,
|
||||
height: number,
|
||||
@@ -267,6 +291,7 @@ function cleanLowConfidenceIsolatedColors(
|
||||
for (let column = 0; column < width; column++) {
|
||||
const index = row * width + column;
|
||||
const current = colors[index];
|
||||
if (!current) continue;
|
||||
if (confidence[index] >= 0.625) continue;
|
||||
const chroma = Math.hypot(current.lab[1], current.lab[2]);
|
||||
const protectedDetail = current.lab[0] < 0.32 || current.lab[0] > 0.96 || chroma > 0.14;
|
||||
@@ -283,6 +308,7 @@ function cleanLowConfidenceIsolatedColors(
|
||||
if (neighborRow < 0 || neighborColumn < 0 || neighborRow >= height || neighborColumn >= width) continue;
|
||||
const neighborIndex = neighborRow * width + neighborColumn;
|
||||
const neighbor = colors[neighborIndex];
|
||||
if (!neighbor) continue;
|
||||
if (neighbor.code === current.code) sameColorNeighbors += 1;
|
||||
const entry = neighborCounts.get(neighbor.code) ?? { color: neighbor, count: 0, confidentCount: 0 };
|
||||
entry.count += 1;
|
||||
@@ -370,11 +396,11 @@ 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 [requestedWidth, setRequestedWidth] = useState(100);
|
||||
const [requestedHeight, setRequestedHeight] = useState(100);
|
||||
const [gridWidth, setGridWidth] = useState(100);
|
||||
const [gridHeight, setGridHeight] = useState(100);
|
||||
const [colorLimit, setColorLimit] = useState(64);
|
||||
const [fitMode, setFitMode] = useState<"cover" | "contain">("cover");
|
||||
const [samplingStrategy, setSamplingStrategy] = useState<SamplingStrategy>("smooth");
|
||||
const [cropZoom, setCropZoom] = useState(1);
|
||||
@@ -383,7 +409,7 @@ export default function Home() {
|
||||
const [pixels, setPixels] = useState<Pixel[]>([]);
|
||||
const [selectedCodes, setSelectedCodes] = useState<Set<string>>(new Set());
|
||||
const [onlySelected, setOnlySelected] = useState(false);
|
||||
const [zoom, setZoom] = useState(24);
|
||||
const [zoom, setZoom] = useState(8);
|
||||
const [showGrid, setShowGrid] = useState(true);
|
||||
const [showCodes, setShowCodes] = useState(true);
|
||||
const [query, setQuery] = useState("");
|
||||
@@ -409,6 +435,7 @@ export default function Home() {
|
||||
const suppressCanvasClickRef = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
logUsage({ event: "page_view" });
|
||||
const saved = localStorage.getItem("bead-pattern-history");
|
||||
if (!saved) return;
|
||||
queueMicrotask(() => {
|
||||
@@ -419,6 +446,7 @@ export default function Home() {
|
||||
const convertImage = (
|
||||
image = sourceImageRef.current,
|
||||
crop = { zoom: cropZoom, x: cropX, y: cropY },
|
||||
recordUsage = true,
|
||||
) => {
|
||||
if (!image) return;
|
||||
const width = Math.max(8, Math.min(256, requestedWidth));
|
||||
@@ -434,15 +462,14 @@ export default function Home() {
|
||||
sample.width = width;
|
||||
sample.height = height;
|
||||
const ctx = sample.getContext("2d", { willReadFrequently: true })!;
|
||||
ctx.fillStyle = "#f7f5ed";
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
drawFittedImage(ctx, image, width, height, fitMode, crop.zoom, crop.x, crop.y);
|
||||
data = ctx.getImageData(0, 0, width, height).data;
|
||||
}
|
||||
const { pixelKeys, clusters, binClusters } = mergePerceptualColors(data);
|
||||
const limitedPalette = chooseDistinctMardColors(clusters, Math.max(2, Math.min(colorLimit, PALETTE.length)));
|
||||
const clusterMatches = clusters.map((cluster) => nearestColor(cluster.lab, limitedPalette));
|
||||
let matchedColors = pixelKeys.map((key) => clusterMatches[binClusters.get(key) ?? 0]);
|
||||
let matchedColors: Array<BeadColor | null> = pixelKeys.map((key) => key === null ? null : clusterMatches[binClusters.get(key) ?? 0]);
|
||||
let cleanedCount = 0;
|
||||
if (dominantConfidence) {
|
||||
const cleaned = cleanLowConfidenceIsolatedColors(matchedColors, dominantConfidence, width, height);
|
||||
@@ -457,8 +484,21 @@ export default function Home() {
|
||||
setPixels(converted);
|
||||
setSelectedCodes(new Set());
|
||||
const strategyName = samplingStrategy === "dominant" ? `区域主色 · 清理 ${cleanedCount} 个低可信孤立格` : "平滑取色";
|
||||
const actualColorCount = new Set(matchedColors.map((color) => color.code)).size;
|
||||
setStatus(`已转换为 ${width} × ${height} · ${strategyName} · ${actualColorCount} 种差异色`);
|
||||
const actualColorCount = new Set(matchedColors.filter((color): color is BeadColor => color !== null).map((color) => color.code)).size;
|
||||
const actualBeadCount = matchedColors.filter((color) => color !== null).length;
|
||||
if (recordUsage) {
|
||||
logUsage({
|
||||
event: "conversion",
|
||||
width,
|
||||
height,
|
||||
sampling_strategy: samplingStrategy,
|
||||
color_limit: colorLimit,
|
||||
actual_colors: actualColorCount,
|
||||
bead_count: actualBeadCount,
|
||||
transparent_cells: width * height - actualBeadCount,
|
||||
});
|
||||
}
|
||||
setStatus(`已转换为 ${width} × ${height} · ${actualBeadCount} 颗拼豆 · ${strategyName} · ${actualColorCount} 种差异色`);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@@ -467,7 +507,7 @@ export default function Home() {
|
||||
image.onload = () => {
|
||||
sourceImageRef.current = image;
|
||||
setSourceUrl(demo);
|
||||
convertImage(image);
|
||||
convertImage(image, undefined, false);
|
||||
};
|
||||
image.src = demo;
|
||||
// This intentionally runs once to create the initial example.
|
||||
@@ -493,12 +533,14 @@ export default function Home() {
|
||||
|
||||
const usedColors = useMemo(() => {
|
||||
const counts = new Map<string, number>();
|
||||
pixels.forEach(({ color }) => counts.set(color.code, (counts.get(color.code) ?? 0) + 1));
|
||||
pixels.forEach(({ color }) => { if (color) counts.set(color.code, (counts.get(color.code) ?? 0) + 1); });
|
||||
return PALETTE.filter((color) => counts.has(color.code))
|
||||
.map((color) => ({ ...color, count: counts.get(color.code)! }))
|
||||
.sort((a, b) => b.count - a.count);
|
||||
}, [pixels]);
|
||||
|
||||
const beadCount = useMemo(() => pixels.reduce((total, pixel) => total + (pixel.color ? 1 : 0), 0), [pixels]);
|
||||
|
||||
const filteredColors = useMemo(() => {
|
||||
const key = query.trim().toLowerCase();
|
||||
return usedColors.filter((color) => !key || color.code.toLowerCase().includes(key) || color.name.includes(key) || color.hex.toLowerCase().includes(key));
|
||||
@@ -541,6 +583,15 @@ export default function Home() {
|
||||
pixels.forEach(({ color }, index) => {
|
||||
const x = ruler + (index % gridWidth) * cell;
|
||||
const y = ruler + Math.floor(index / gridWidth) * cell;
|
||||
if (!color) {
|
||||
ctx.clearRect(x, y, cell, cell);
|
||||
if (showGrid) {
|
||||
ctx.strokeStyle = "rgba(32,38,36,.12)";
|
||||
ctx.lineWidth = 0.6;
|
||||
ctx.strokeRect(x + 0.3, y + 0.3, cell - 0.6, cell - 0.6);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const hasSelection = selectedCodes.size > 0;
|
||||
const isSelected = selectedCodes.has(color.code);
|
||||
if (onlySelected && hasSelection && !isSelected) {
|
||||
@@ -565,6 +616,18 @@ export default function Home() {
|
||||
ctx.fillText(color.code, x + cell / 2, y + cell / 2);
|
||||
}
|
||||
});
|
||||
if (showGrid) {
|
||||
ctx.strokeStyle = "rgba(32,38,36,.48)";
|
||||
ctx.lineWidth = 1.5;
|
||||
for (let column = 5; column < gridWidth; column += 5) {
|
||||
const x = ruler + column * cell;
|
||||
ctx.beginPath(); ctx.moveTo(x, ruler); ctx.lineTo(x, ruler + gridHeight * cell); ctx.stroke();
|
||||
}
|
||||
for (let row = 5; row < gridHeight; row += 5) {
|
||||
const y = ruler + row * cell;
|
||||
ctx.beginPath(); ctx.moveTo(ruler, y); ctx.lineTo(ruler + gridWidth * cell, y); ctx.stroke();
|
||||
}
|
||||
}
|
||||
}, [pixels, gridWidth, gridHeight, zoom, selectedCodes, onlySelected, showGrid, showCodes]);
|
||||
|
||||
const handleUpload = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
@@ -686,7 +749,7 @@ export default function Home() {
|
||||
const x = Math.floor((event.clientX - rect.left - ruler) / zoom);
|
||||
const y = Math.floor((event.clientY - rect.top - ruler) / zoom);
|
||||
const pixel = pixels[y * gridWidth + x];
|
||||
if (x >= 0 && y >= 0 && x < gridWidth && y < gridHeight && pixel) toggleColor(pixel.color.code);
|
||||
if (x >= 0 && y >= 0 && x < gridWidth && y < gridHeight && pixel?.color) toggleColor(pixel.color.code);
|
||||
};
|
||||
|
||||
const handleCanvasMove = (event: React.MouseEvent<HTMLCanvasElement>) => {
|
||||
@@ -701,7 +764,7 @@ export default function Home() {
|
||||
const column = Math.floor((event.clientX - rect.left - ruler) / zoom);
|
||||
const row = Math.floor((event.clientY - rect.top - ruler) / zoom);
|
||||
const pixel = pixels[row * gridWidth + column];
|
||||
if (column < 0 || row < 0 || column >= gridWidth || row >= gridHeight || !pixel) {
|
||||
if (column < 0 || row < 0 || column >= gridWidth || row >= gridHeight || !pixel?.color) {
|
||||
setHoveredPixel(null);
|
||||
return;
|
||||
}
|
||||
@@ -722,7 +785,7 @@ export default function Home() {
|
||||
width: gridWidth,
|
||||
height: gridHeight,
|
||||
colorLimit,
|
||||
codes: pixels.map((pixel) => pixel.color.code),
|
||||
codes: pixels.map((pixel) => pixel.color?.code ?? null),
|
||||
};
|
||||
persistHistory([entry, ...history].slice(0, 30));
|
||||
setStatus("图纸已保存到本机历史记录");
|
||||
@@ -730,7 +793,7 @@ export default function Home() {
|
||||
|
||||
const restorePattern = (entry: SavedPattern) => {
|
||||
const colorMap = new Map(PALETTE.map((color) => [color.code, color]));
|
||||
setPixels(entry.codes.map((code) => ({ color: colorMap.get(code) ?? PALETTE[0] })));
|
||||
setPixels(entry.codes.map((code) => ({ color: code === null ? null : colorMap.get(code) ?? PALETTE[0] })));
|
||||
setGridWidth(entry.width);
|
||||
setGridHeight(entry.height);
|
||||
setRequestedWidth(entry.width);
|
||||
@@ -868,7 +931,7 @@ export default function Home() {
|
||||
</section>
|
||||
|
||||
<aside className="color-panel">
|
||||
<div className="panel-heading"><span>03</span><div><h2>分色查看</h2><p>{usedColors.length} 种颜色 · {pixels.length} 颗</p></div></div>
|
||||
<div className="panel-heading"><span>03</span><div><h2>分色查看</h2><p>{usedColors.length} 种颜色 · {beadCount} 颗</p></div></div>
|
||||
<div className="search-box"><span>⌕</span><input value={query} onChange={(e) => setQuery(e.target.value)} placeholder="输入 A6、MARD A 或 #FEAC4C" /></div>
|
||||
<div className="selection-tools">
|
||||
<label className="switch-row"><input type="checkbox" checked={onlySelected} onChange={(e) => setOnlySelected(e.target.checked)} /><span className="switch" /><span>只显示选中颜色</span></label>
|
||||
@@ -894,7 +957,7 @@ export default function Home() {
|
||||
</div>
|
||||
{filteredHistory.length ? <div className="history-grid">{filteredHistory.map((entry) => {
|
||||
const colorCounts = new Map<string, number>();
|
||||
entry.codes.forEach((code) => colorCounts.set(code, (colorCounts.get(code) ?? 0) + 1));
|
||||
entry.codes.forEach((code) => { if (code) colorCounts.set(code, (colorCounts.get(code) ?? 0) + 1); });
|
||||
const topCodes = [...colorCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 6);
|
||||
return <article className="history-card" key={entry.id}>
|
||||
<div className="history-swatches">{topCodes.map(([code, count]) => <i key={code} style={{ backgroundColor: PALETTE.find((color) => color.code === code)?.hex, flexGrow: count }} />)}</div>
|
||||
|
||||
+56
-1
@@ -1,10 +1,14 @@
|
||||
import { createReadStream, existsSync, statSync } from "node:fs";
|
||||
import { appendFile, createReadStream, existsSync, mkdirSync, statSync } from "node:fs";
|
||||
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 });
|
||||
const mimeTypes = {
|
||||
".css": "text/css; charset=utf-8",
|
||||
".html": "text/html; charset=utf-8",
|
||||
@@ -17,8 +21,59 @@ 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 entry = {
|
||||
timestamp: new Date().toISOString(),
|
||||
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");
|
||||
|
||||
|
||||
@@ -4,3 +4,5 @@ services:
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "${APP_PORT:-3200}:3200"
|
||||
volumes:
|
||||
- ./data:/app/data
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"name": "rolldown-runtime"
|
||||
},
|
||||
"app/page.tsx": {
|
||||
"file": "_next/static/chunks/page-DuaIrYp1.js",
|
||||
"file": "_next/static/chunks/page-OnKjbyiO.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-D3q3zeiS.js",
|
||||
"file": "_next/static/chunks/layout-segment-context-a2puOOKn.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-CYDm4YY8.js",
|
||||
"file": "_next/static/chunks/index-DJm5cxJ6.js",
|
||||
"name": "index",
|
||||
"src": "virtual:vinext-app-browser-entry",
|
||||
"isEntry": true,
|
||||
|
||||
+2
-2
@@ -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.JNuJjVb4.css" data-rsc-css-href="/_next/static/css/index.JNuJjVb4.css" data-precedence="vite-rsc/importer-resources"/><link rel="modulepreload" fetchPriority="low" href="/_next/static/chunks/index-CYDm4YY8.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-CYDm4YY8.js" />
|
||||
</head><body><div style="font-family:system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";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-CYDm4YY8.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.JNuJjVb4.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.JNuJjVb4.css\",{\"rel\":\"stylesheet\",\"precedence\":\"vite-rsc/importer-resources\",\"href\":\"/_next/static/css/index.JNuJjVb4.css\",\"data-rsc-css-href\":\"/_next/static/css/index.JNuJjVb4.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.B96d0yxr.css" data-rsc-css-href="/_next/static/css/index.B96d0yxr.css" data-precedence="vite-rsc/importer-resources"/><link rel="modulepreload" fetchPriority="low" href="/_next/static/chunks/index-DJm5cxJ6.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-DJm5cxJ6.js" />
|
||||
</head><body><div style="font-family:system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";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-DJm5cxJ6.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.B96d0yxr.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.B96d0yxr.css\",{\"rel\":\"stylesheet\",\"precedence\":\"vite-rsc/importer-resources\",\"href\":\"/_next/static/css/index.B96d0yxr.css\",\"data-rsc-css-href\":\"/_next/static/css/index.B96d0yxr.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>
|
||||
+3
-3
File diff suppressed because one or more lines are too long
+1
-1
@@ -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-CYDm4YY8.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-DJm5cxJ6.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
File diff suppressed because one or more lines are too long
+2
-2
@@ -6,8 +6,8 @@
|
||||
7:I["8c0f216c4604",[],"Slot",1]
|
||||
8:I["9276801271d6",[],"AppRouterScrollTarget",1]
|
||||
9:I["593f344dc510",[],"RedirectBoundary",1]
|
||||
:HL["/_next/static/css/index.JNuJjVb4.css","style" ]
|
||||
0:{"__route":"route:/","__interceptionContext":null,"__layoutIds":["layout:/"],"__rootLayout":"/","__sourcePage":"/page","page:/":"$L1","layout:/":[[[["$","link","css:/_next/static/css/index.JNuJjVb4.css",{"rel":"stylesheet","precedence":"vite-rsc/importer-resources","href":"/_next/static/css/index.JNuJjVb4.css","data-rsc-css-href":"/_next/static/css/index.JNuJjVb4.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":"c795b2fa-f236-43d0-8c60-3bd049510d5e","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.B96d0yxr.css","style" ]
|
||||
0:{"__route":"route:/","__interceptionContext":null,"__layoutIds":["layout:/"],"__rootLayout":"/","__sourcePage":"/page","page:/":"$L1","layout:/":[[[["$","link","css:/_next/static/css/index.B96d0yxr.css",{"rel":"stylesheet","precedence":"vite-rsc/importer-resources","href":"/_next/static/css/index.B96d0yxr.css","data-rsc-css-href":"/_next/static/css/index.B96d0yxr.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":"b9f856ce-42d7-4216-b786-6bd488655129","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,3 +1,3 @@
|
||||
{
|
||||
"appBrowserEntry": "_next/static/chunks/index-CYDm4YY8.js"
|
||||
"appBrowserEntry": "_next/static/chunks/index-DJm5cxJ6.js"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user