feat(card): Marken-Logos entfernt, Community-Logo unten rechts im Renderer
- card/config.ts: network und das Apple-Logo entfallen. CardConfig.logo ist die ID eines Community-Logos (Muster ID_PATTERN,
sonst null), DEFAULT_CONFIG.logo = null, serializeConfig schreibt {v, template, logo, details, brand, crop}. Ein altes network
wird ignoriert, brand 'apple' fällt auf den Standard. ID_PATTERN steht jetzt hier (die reine Konfiguration prüft die Logo-ID,
ohne Appwrite zu laden), account/cards.ts reicht sie weiter; single-sources.test.ts sorgt für genau eine Definition.
- card/geometry.ts: fitLogo und LOGO_MAX_H (125) ersetzen fitNetwork, Layout.network heißt Layout.logo.
- card/marks.ts: nur noch die AirCard-IOS-Marken (BRAND_MARKS, getBrandMark, svgAspect, tintSvg, loadMarkImage). Die 19 SVGs
der Netzwerke und von Apple sind per git rm gelöscht, ein Test hält assets/marks auf die zwei AirCard-Dateien.
- card/render.ts: LogoAsset, CardAssets.logo statt network, LoadedAssets.logoMissing. loadCardAssets(config, image, loadLogo =
loadLogoAsset): ohne Logo kein Aufruf, null vom Lader ergibt logoMissing, ein Ladefehler wirft. Der Renderer zeichnet das
Logo mit fitLogo(w / h) unten rechts, wenn Vorlage und Bilder eines haben.
- logos/logo-asset.ts (neu): loadLogoAsset(id, tone) holt Zeile (getLogo) und PNG (loadLogoBlob) und dekodiert mit
createImageBitmap, sonst über ein Image. Einfarbige Logos werden in einem eigenen Canvas mit source-in in der Details-Farbe
gefüllt, mehrfarbige bleiben. Fehlende Zeile, ausgeblendetes Logo (auch für Hochlader und Admins), Datei 404, unlesbare
Datei und mehr als 1 000 000 Pixel ergeben null. Je (id, tone) ein Ladevorgang, ein Fehler bleibt nicht im Cache.
rememberLogo, forgetLogo und LOGO_MISSING nach der Schnittstelle des Plans.
- editor/options-panel.ts: keine Netzwerk-Kacheln mehr, Überschrift „Logo oben links“ mit Symbol, Text und Kein Logo (ohne
Apple), dazu logoSlot als leerer Platz oben für Task 8 (editor.css: Details rückt bei leerem Platz wie bisher nach oben).
- editor/editor-page.ts: logoKey aus Logo, Details und Marke, der Logo-Zustand hält missing. Fehlt das Logo, wird die Karte
ohne es gezeichnet und der Hinweis LOGO_MISSING gezeigt, solange das so bleibt; ein Wechsel auf ein vorhandenes Logo oder
auf kein Logo nimmt nur diesen Hinweis weg (PNG-Größe und „Bild fehlt“ stehen getrennt und bleiben). Kein LOGO_ERROR bei
fehlendem Logo, nur bei echtem Ladefehler, auch beim Download.
- community/publish.ts: LOGO_MISSING_PUBLISH, renderForGallery (jetzt exportiert) bricht bei logoMissing vor dem Rendern ab,
es wird nichts hochgeladen. publish-dialog.ts lässt den Text im Wortlaut durch.
- i18n/en.ts: englische Texte zu LOGO_MISSING, LOGO_MISSING_PUBLISH und „Logo oben links“.
- Tests: config, geometry, marks, render und die Fixtures von cards, theme, cards-page auf das neue Modell; neu
logo-asset.test.ts (28) und options-panel.test.ts (10); editor-page (14 neue), publish und publish-dialog erweitert.
Vitest 45 Dateien, 1319 Tests grün, tsc und Build sauber.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3051b3b3f9
commit
0be5abd954
44 files changed
+1401
-366
No files matched your search
+8
-54
@@ -1,35 +1,17 @@
|
||||
// Logo-Register: SVG-Dateien der Netzwerke und Marken, Einfärben und Laden als Bild für den Canvas.
|
||||
// Logo-Register: SVG-Dateien der AirCard-IOS-Marken (Logo oben links), Einfärben und Laden als Bild für den Canvas.
|
||||
// Reine Daten plus ein kleiner Browser-Lader; die Größe der Logos bestimmt allein die viewBox.
|
||||
// Die Logos unten rechts kommen von der Community (logos/logo-asset.ts), hier steht keine fremde Marke.
|
||||
|
||||
import { TONE_COLOR, type BrandId, type DetailsTone, type NetworkId, NETWORK_IDS } from './config';
|
||||
import { TONE_COLOR, type BrandId, type DetailsTone } from './config';
|
||||
|
||||
import visaAuto from '../assets/marks/visa-auto.svg?raw';
|
||||
import visaBlue from '../assets/marks/visa-blue.svg?raw';
|
||||
import visaNavy from '../assets/marks/visa-navy.svg?raw';
|
||||
import visaSilver from '../assets/marks/visa-silver.svg?raw';
|
||||
import mcColor from '../assets/marks/mc-color.svg?raw';
|
||||
import mcSilver from '../assets/marks/mc-silver.svg?raw';
|
||||
import mcWordmark from '../assets/marks/mc-wordmark.svg?raw';
|
||||
import mcSilverText from '../assets/marks/mc-silver-text.svg?raw';
|
||||
import mcClassic from '../assets/marks/mc-classic.svg?raw';
|
||||
import upColor from '../assets/marks/up-color.svg?raw';
|
||||
import upSilver from '../assets/marks/up-silver.svg?raw';
|
||||
import upWhiteFrame from '../assets/marks/up-white-frame.svg?raw';
|
||||
import upEnglish from '../assets/marks/up-english.svg?raw';
|
||||
import amexBlue from '../assets/marks/amex-blue.svg?raw';
|
||||
import amexOutline from '../assets/marks/amex-outline.svg?raw';
|
||||
import amexSelect from '../assets/marks/amex-select.svg?raw';
|
||||
import jcb from '../assets/marks/jcb.svg?raw';
|
||||
import discover from '../assets/marks/discover.svg?raw';
|
||||
import aircardsSymbol from '../assets/marks/aircards-symbol.svg?raw';
|
||||
import aircardsText from '../assets/marks/aircards-text.svg?raw';
|
||||
import apple from '../assets/marks/apple.svg?raw';
|
||||
|
||||
export type MarkGroup = 'Visa' | 'Mastercard' | 'UnionPay' | 'American Express' | 'JCB' | 'Discover' | 'Logo';
|
||||
export type MarkGroup = 'Logo';
|
||||
|
||||
export interface MarkDef {
|
||||
id: NetworkId | Exclude<BrandId, 'none'>;
|
||||
/** Gruppenname; zusammen mit dem Label der Text für `aria-label`/`title` („Visa – Blau“). */
|
||||
id: Exclude<BrandId, 'none'>;
|
||||
/** Gruppenname; zusammen mit dem Label der Text für `aria-label`/`title` („Logo – AirCard-IOS · Symbol“). */
|
||||
group: MarkGroup;
|
||||
label: string;
|
||||
svg: string;
|
||||
@@ -39,45 +21,17 @@ export interface MarkDef {
|
||||
maxH: number;
|
||||
}
|
||||
|
||||
type NetworkEntry = Omit<MarkDef, 'id'>;
|
||||
type MarkEntry = Omit<MarkDef, 'id'>;
|
||||
|
||||
const NETWORK_TABLE: Record<NetworkId, NetworkEntry> = {
|
||||
'visa-auto': { group: 'Visa', label: 'Automatisch', svg: visaAuto, tint: true, maxH: 109 },
|
||||
'visa-blue': { group: 'Visa', label: 'Blau', svg: visaBlue, tint: false, maxH: 109 },
|
||||
'visa-navy': { group: 'Visa', label: 'Marineblau', svg: visaNavy, tint: false, maxH: 109 },
|
||||
'visa-silver': { group: 'Visa', label: 'Metallisches Silber', svg: visaSilver, tint: false, maxH: 109 },
|
||||
'mc-color': { group: 'Mastercard', label: 'Farbe', svg: mcColor, tint: false, maxH: 125 },
|
||||
'mc-silver': { group: 'Mastercard', label: 'Metallisches Silber', svg: mcSilver, tint: false, maxH: 125 },
|
||||
'mc-wordmark': { group: 'Mastercard', label: 'Wortmarke', svg: mcWordmark, tint: false, maxH: 109 },
|
||||
'mc-silver-text': { group: 'Mastercard', label: 'Silber + Text', svg: mcSilverText, tint: false, maxH: 109 },
|
||||
'mc-classic': { group: 'Mastercard', label: 'Klassisch', svg: mcClassic, tint: false, maxH: 125 },
|
||||
'up-color': { group: 'UnionPay', label: 'Farbe', svg: upColor, tint: false, maxH: 125 },
|
||||
'up-silver': { group: 'UnionPay', label: 'Metallisches Silber', svg: upSilver, tint: false, maxH: 125 },
|
||||
'up-white-frame': { group: 'UnionPay', label: 'Weißer Rahmen', svg: upWhiteFrame, tint: false, maxH: 125 },
|
||||
'up-english': { group: 'UnionPay', label: 'Englisch', svg: upEnglish, tint: false, maxH: 109 },
|
||||
'amex-blue': { group: 'American Express', label: 'Blau', svg: amexBlue, tint: false, maxH: 125 },
|
||||
'amex-outline': { group: 'American Express', label: 'Umriss', svg: amexOutline, tint: true, maxH: 125 },
|
||||
'amex-select': { group: 'American Express', label: 'Select', svg: amexSelect, tint: false, maxH: 90 },
|
||||
jcb: { group: 'JCB', label: 'JCB', svg: jcb, tint: false, maxH: 125 },
|
||||
discover: { group: 'Discover', label: 'Discover', svg: discover, tint: false, maxH: 109 },
|
||||
};
|
||||
|
||||
const BRAND_TABLE: Record<Exclude<BrandId, 'none'>, NetworkEntry> = {
|
||||
const BRAND_TABLE: Record<Exclude<BrandId, 'none'>, MarkEntry> = {
|
||||
'aircards-symbol': { group: 'Logo', label: 'AirCard-IOS · Symbol', svg: aircardsSymbol, tint: true, maxH: 100 },
|
||||
'aircards-text': { group: 'Logo', label: 'AirCard-IOS · Text', svg: aircardsText, tint: true, maxH: 100 },
|
||||
apple: { group: 'Logo', label: 'Apple', svg: apple, tint: true, maxH: 100 },
|
||||
};
|
||||
|
||||
export const NETWORK_MARKS: readonly MarkDef[] = NETWORK_IDS.map((id) => ({ id, ...NETWORK_TABLE[id] }));
|
||||
|
||||
export const BRAND_MARKS: readonly MarkDef[] = (
|
||||
Object.keys(BRAND_TABLE) as Exclude<BrandId, 'none'>[]
|
||||
).map((id) => ({ id, ...BRAND_TABLE[id] }));
|
||||
|
||||
export function getNetworkMark(id: NetworkId): MarkDef {
|
||||
return NETWORK_MARKS.find((m) => m.id === id) ?? NETWORK_MARKS[0];
|
||||
}
|
||||
|
||||
export function getBrandMark(id: Exclude<BrandId, 'none'>): MarkDef {
|
||||
return BRAND_MARKS.find((m) => m.id === id) ?? BRAND_MARKS[0];
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user