refactor(md-deck): pos string and yaml copy output

Replace per-axis x1/y1/x2/y2 layer fields with a single
pos string (x1,y1-x2,y2) matching the compact layers format.

When a deck is configured via a yaml/tag codeblock (data-config),
the copy button now emits a yaml/tag block serialized from current
store state so template layers round-trip.
This commit is contained in:
2026-09-02 18:30:32 +08:00
parent 16dfc8a88c
commit f1ddc8fb8e
7 changed files with 100 additions and 33 deletions
+55
View File
@@ -1,4 +1,5 @@
import { createStore } from "solid-js/store";
import yaml from "js-yaml";
import { calculateDimensions } from "./dimensions";
import { loadCSV, CSV } from "../../utils/csv-loader";
import { formatLayers } from "./layer-parser";
@@ -41,6 +42,8 @@ export interface DeckState {
cornerRadius: number;
shape: CardShape;
fixed: boolean;
/** True when the deck was configured via a yaml/tag codeblock (data-config). */
isYamlBlock: boolean;
src: string;
rawSrc: string;
@@ -85,6 +88,7 @@ export interface DeckActions {
setPadding: (padding: number) => void;
setCornerRadius: (cornerRadius: number) => void;
setShape: (shape: CardShape) => void;
setIsYamlBlock: (isYamlBlock: boolean) => void;
setCards: (cards: CSV<CardData>) => void;
setActiveTab: (index: number) => void;
@@ -176,6 +180,7 @@ export function createDeckStore(initialSrc: string = ""): DeckStore {
cornerRadius: DECK_DEFAULTS.CORNER_RADIUS,
shape: "rectangle",
fixed: false,
isYamlBlock: false,
src: initialSrc,
rawSrc: initialSrc,
dimensions: null,
@@ -244,6 +249,9 @@ export function createDeckStore(initialSrc: string = ""): DeckStore {
const setShape = (shape: CardShape) => {
setState({ shape });
};
const setIsYamlBlock = (isYamlBlock: boolean) => {
setState({ isYamlBlock });
};
const setCards = (cards: CSV<CardData>) => setState({ cards, activeTab: 0 });
const setActiveTab = (index: number) => setState({ activeTab: index });
@@ -483,6 +491,9 @@ export function createDeckStore(initialSrc: string = ""): DeckStore {
const clearError = () => setState({ error: null });
const generateCode = (backLayersStr?: string) => {
if (state.isYamlBlock) {
return generateYamlCode();
}
const frontLayersStr = formatLayers(state.frontLayerConfigs);
const backLayersString =
backLayersStr || formatLayers(state.backLayerConfigs);
@@ -510,6 +521,49 @@ export function createDeckStore(initialSrc: string = ""): DeckStore {
return parts.join("");
};
/** Serialize the deck back to a ```yaml/tag codeblock (round-trips templates). */
const generateYamlCode = () => {
const toYamlLayer = (l: LayerConfig) => {
const out: Record<string, unknown> = {};
if (l.template) {
out.template = l.template;
} else {
out.prop = l.prop ?? "";
}
out.pos = `${l.x1},${l.y1}-${l.x2},${l.y2}`;
if (l.fontSize) out.font = l.fontSize;
if (l.orientation && l.orientation !== "n") out.orientation = l.orientation;
if (l.align) out.align = l.align;
if (!l.visible) out.visible = false;
return out;
};
const config: Record<string, unknown> = {
size: `${state.sizeW}x${state.sizeH}`,
grid: `${state.gridW}x${state.gridH}`,
layers: state.frontLayerConfigs.map(toYamlLayer),
};
if (state.bleed !== DECK_DEFAULTS.BLEED) config.bleed = state.bleed;
if (state.padding !== DECK_DEFAULTS.PADDING) config.padding = state.padding;
if (state.shape !== "rectangle") config.shape = state.shape;
if (state.backLayerConfigs.length > 0) {
config.back_layers = state.backLayerConfigs.map(toYamlLayer);
}
const doc = {
tag: "md-deck",
body: state.rawSrc || state.src,
"data-config": config,
};
const yamlStr = yaml.dump(doc, {
indent: 2,
lineWidth: -1,
noRefs: true,
});
const fence = "```";
return `${fence}yaml/tag\n${yamlStr}${fence}`;
};
const copyCode = async (fallback?: (code: string) => void) => {
const code = generateCode();
try {
@@ -569,6 +623,7 @@ export function createDeckStore(initialSrc: string = ""): DeckStore {
setPadding,
setCornerRadius,
setShape,
setIsYamlBlock,
setCards,
setActiveTab,
updateCardData,