Compare commits

..

No commits in common. "6fcd879287a38973a4c3fced07fdfb3c02833050" and "b2e10d847e1e8113043612f692db4809e56375ed" have entirely different histories.

7 changed files with 51 additions and 87 deletions

View File

@ -1,10 +1,9 @@
import {createMemo, For} from 'solid-js'; import {createMemo, For} from 'solid-js';
import {parseMarkdown} from '../../markdown'; import { marked } from '../../markdown';
import { getLayerStyle } from './hooks/dimensions'; import { getLayerStyle } from './hooks/dimensions';
import type { CardData } from './types'; import type { CardData } from './types';
import {DeckStore} from "./hooks/deckStore"; import {DeckStore} from "./hooks/deckStore";
import {processVariables} from "../utils/csv-loader"; import {processVariables} from "../utils/csv-loader";
import {resolvePath} from "../utils/path";
export interface CardLayerProps { export interface CardLayerProps {
cardData: CardData; cardData: CardData;
@ -17,8 +16,7 @@ export function CardLayer(props: CardLayerProps) {
const showBounds = () => props.store.state.isEditing; const showBounds = () => props.store.state.isEditing;
function renderLayerContent(content: string) { function renderLayerContent(content: string) {
const iconPath = resolvePath(props.store.state.cards.sourcePath, props.cardData.iconPath); return marked.parse(processVariables(content, props.cardData, props.store.state.cards)) as string;
return parseMarkdown(processVariables(content, props.cardData, props.store.state.cards), iconPath) as string;
} }
return ( return (
<For each={layers()}> <For each={layers()}>

View File

@ -49,48 +49,44 @@ export function LayerEditorPanel(props: LayerEditorPanelProps) {
/> />
<span class="text-sm flex-1">{layer.prop}</span> <span class="text-sm flex-1">{layer.prop}</span>
</div> </div>
{layer.visible && ( <button
<>
<button
onClick={() => store.actions.setEditingLayer(store.state.editingLayer === layer.prop ? null : layer.prop)} onClick={() => store.actions.setEditingLayer(store.state.editingLayer === layer.prop ? null : layer.prop)}
class={`text-xs px-2 py-1 rounded cursor-pointer ${ class={`text-xs px-2 py-1 rounded cursor-pointer ${
store.state.editingLayer === layer.prop store.state.editingLayer === layer.prop
? 'bg-blue-500 text-white' ? 'bg-blue-500 text-white'
: 'bg-gray-200 text-gray-700 hover:bg-gray-300' : 'bg-gray-200 text-gray-700 hover:bg-gray-300'
}`} }`}
> >
{store.state.editingLayer === layer.prop ? '✓ 框选' : '框选'} {store.state.editingLayer === layer.prop ? '✓ 框选' : '框选'}
</button> </button>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<select <select
value={layer.orientation || 'n'} value={layer.orientation || 'n'}
onChange={(e) => updateLayerOrientation(layer.prop, e.target.value as 'n' | 's' | 'e' | 'w')} onChange={(e) => updateLayerOrientation(layer.prop, e.target.value as 'n' | 's' | 'e' | 'w')}
class="text-xs px-2 py-1 rounded border border-gray-300 bg-white cursor-pointer" class="text-xs px-2 py-1 rounded border border-gray-300 bg-white cursor-pointer"
> >
<For each={ORIENTATION_OPTIONS}> <For each={ORIENTATION_OPTIONS}>
{(opt) => ( {(opt) => (
<option value={opt.value}>{opt.label}</option> <option value={opt.value}>{opt.label}</option>
)} )}
</For> </For>
</select> </select>
</div> </div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<label class="text-xs text-gray-600">/mm</label> <label class="text-xs text-gray-600">/mm</label>
<input <input
type="number" type="number"
value={layer.fontSize || ''} value={layer.fontSize || ''}
placeholder="默认" placeholder="默认"
onChange={(e) => { onChange={(e) => {
const value = e.target.value; const value = e.target.value;
updateLayerFontSize(layer.prop, value ? Number(value) : undefined); updateLayerFontSize(layer.prop, value ? Number(value) : undefined);
}} }}
class="w-16 text-xs px-2 py-1 rounded border border-gray-300 bg-white" class="w-16 text-xs px-2 py-1 rounded border border-gray-300 bg-white"
step="0.1" step="0.1"
min="0.1" min="0.1"
/> />
</div> </div>
</>
)}
</div> </div>
)} )}
</For> </For>

View File

@ -74,7 +74,7 @@ export interface DeckActions {
setPadding: (padding: number) => void; setPadding: (padding: number) => void;
// 数据设置 // 数据设置
setCards: (cards: CSV<CardData>) => void; setCards: (cards: CardData[]) => void;
setActiveTab: (index: number) => void; setActiveTab: (index: number) => void;
updateCardData: (index: number, key: string, value: string) => void; updateCardData: (index: number, key: string, value: string) => void;
@ -139,7 +139,7 @@ export function createDeckStore(
src: initialSrc, src: initialSrc,
rawSrc: initialSrc, rawSrc: initialSrc,
dimensions: null, dimensions: null,
cards: [] as any, cards: [],
activeTab: 0, activeTab: 0,
layerConfigs: [], layerConfigs: [],
isEditing: false, isEditing: false,
@ -195,7 +195,7 @@ export function createDeckStore(
updateDimensions(); updateDimensions();
}; };
const setCards = (cards: CSV<CardData>) => setState({ cards, activeTab: 0 }); const setCards = (cards: CardData[]) => setState({ cards, activeTab: 0 });
const setActiveTab = (index: number) => setState({ activeTab: index }); const setActiveTab = (index: number) => setState({ activeTab: index });
const updateCardData = (index: number, key: string, value: string) => { const updateCardData = (index: number, key: string, value: string) => {
setState('cards', index, key, value); setState('cards', index, key, value);
@ -269,22 +269,7 @@ export function createDeckStore(
const generateCode = () => { const generateCode = () => {
const layersStr = formatLayers(state.layerConfigs); const layersStr = formatLayers(state.layerConfigs);
const parts = [ return `:md-deck[${state.rawSrc || state.src}]{size="${state.sizeW}x${state.sizeH}" grid="${state.gridW}x${state.gridH}" bleed="${state.bleed}" padding="${state.padding}" layers="${layersStr}"}`;
`:md-deck[${state.rawSrc || state.src}]`,
`{size="${state.sizeW}x${state.sizeH} "`,
`grid="${state.gridW}x${state.gridH} "`
];
// 仅在非默认值时添加 bleed 和 padding
if (state.bleed !== DECK_DEFAULTS.BLEED) {
parts.push(`bleed="${state.bleed} "`);
}
if (state.padding !== DECK_DEFAULTS.PADDING) {
parts.push(`padding="${state.padding} "`);
}
parts.push(`layers="${layersStr}"}`);
return parts.join('');
}; };
const copyCode = async () => { const copyCode = async () => {

View File

@ -57,6 +57,9 @@ export function initLayerConfigs(
): LayerConfig[] { ): LayerConfig[] {
const parsed = parseLayers(existingLayersStr); const parsed = parseLayers(existingLayersStr);
const allProps = Object.keys(data[0] || {}).filter(k => k !== 'label'); const allProps = Object.keys(data[0] || {}).filter(k => k !== 'label');
if(data.frontmatter){
allProps.push(...Object.keys(data.frontmatter));
}
return allProps.map(prop => { return allProps.map(prop => {
const existing = parsed.find(l => l.prop === prop); const existing = parsed.find(l => l.prop === prop);

View File

@ -68,10 +68,9 @@ export async function loadCSV<T = Record<string, string>>(path: string): Promise
if (frontmatter) { if (frontmatter) {
csvResult.frontmatter = frontmatter; csvResult.frontmatter = frontmatter;
for(const each of result){ for(const each of result){
Object.assign(each, frontmatter); Object.setPrototypeOf(each, frontmatter);
} }
} }
csvResult.sourcePath = path;
csvCache.set(path, result); csvCache.set(path, result);
return csvResult; return csvResult;
@ -83,7 +82,6 @@ interface JSONObject extends Record<string, JSONData> {}
export type CSV<T> = T[] & { export type CSV<T> = T[] & {
frontmatter?: JSONObject; frontmatter?: JSONObject;
sourcePath: string;
} }
export function processVariables<T extends JSONObject> (body: string, currentRow: T, csv: CSV<T>, filtered?: T[], remix?: boolean): string { export function processVariables<T extends JSONObject> (body: string, currentRow: T, csv: CSV<T>, filtered?: T[], remix?: boolean): string {
@ -99,5 +97,5 @@ export function processVariables<T extends JSONObject> (body: string, currentRow
return `{{${key}}}`; return `{{${key}}}`;
} }
return body?.replace(/\{\{(\w+)\}\}/g, (_, key) => `${replaceProp(key)}`) || ''; return body.replace(/\{\{(\w+)\}\}/g, (_, key) => `${replaceProp(key)}`);
} }

View File

@ -5,15 +5,6 @@ import markedAlert from "marked-alert";
import markedMermaid from "./mermaid"; import markedMermaid from "./mermaid";
import {gfmHeadingId} from "marked-gfm-heading-id"; import {gfmHeadingId} from "marked-gfm-heading-id";
let globalIconPrefix: string | undefined = undefined;
function overrideIconPrefix(path?: string){
globalIconPrefix = path;
return {
[Symbol.dispose](){
globalIconPrefix = undefined;
}
}
}
// 使用 marked-directive 来支持指令语法 // 使用 marked-directive 来支持指令语法
const marked = new Marked() const marked = new Marked()
.use(gfmHeadingId()) .use(gfmHeadingId())
@ -35,8 +26,7 @@ const marked = new Marked()
// :[blah] becomes <i class="icon icon-blah"></i> // :[blah] becomes <i class="icon icon-blah"></i>
renderer(token) { renderer(token) {
if (!token.meta.name) { if (!token.meta.name) {
const style = globalIconPrefix ? `style="--icon-src: url('${globalIconPrefix}/${token.text}.png')"` : ''; return `<icon class="icon-${token.text}"></icon>`;
return `<icon ${style} class="icon-${token.text}"></icon>`;
} }
return false; return false;
} }
@ -99,9 +89,8 @@ const marked = new Marked()
}] }]
}); });
export function parseMarkdown(content: string, iconPrefix?: string): string { export function parseMarkdown(content: string): string {
using prefix = overrideIconPrefix(iconPrefix); return marked.parse(content.trimStart()) as string;
return marked.parse(content.trimStart()) as string;
} }
export { marked }; export { marked };

View File

@ -2,21 +2,16 @@
@plugin "@tailwindcss/typography"; @plugin "@tailwindcss/typography";
/* icon */ /* icon */
icon, pull{ icon{
@apply inline-block; @apply inline-block;
width: 1em; width: 1em;
height: 1.27em; height: 1em;
vertical-align: text-bottom;
--icon-src: ''; --icon-src: '';
background-image: var(--icon-src); background: var(--icon-src);
background-size: contain; background-size: contain;
background-position: center; background-position: center;
background-repeat: no-repeat; background-repeat: no-repeat;
} }
pull{
margin-right: -.5em;
width: 0;
}
/* prose */ /* prose */