d25d482dc2
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
96 lines
2.8 KiB
TypeScript
96 lines
2.8 KiB
TypeScript
/**
|
|
* Theme parser — extracts color scheme and font definitions from a:theme XML.
|
|
*/
|
|
|
|
import type { SafeXmlNode } from '../parser/xml-parser'
|
|
|
|
export interface ThemeData {
|
|
colorScheme: Map<string, string>
|
|
majorFont: { latin: string; ea: string; cs: string }
|
|
minorFont: { latin: string; ea: string; cs: string }
|
|
fillStyles: SafeXmlNode[] // from a:fillStyleLst children (indexed 1-based)
|
|
lineStyles: SafeXmlNode[] // from a:lnStyleLst children (indexed 1-based)
|
|
effectStyles: SafeXmlNode[] // from a:effectStyleLst children (indexed 1-based)
|
|
}
|
|
|
|
/** Known color scheme slot names in a:clrScheme. */
|
|
const COLOR_SLOTS = [
|
|
'dk1',
|
|
'dk2',
|
|
'lt1',
|
|
'lt2',
|
|
'accent1',
|
|
'accent2',
|
|
'accent3',
|
|
'accent4',
|
|
'accent5',
|
|
'accent6',
|
|
'hlink',
|
|
'folHlink',
|
|
] as const
|
|
|
|
/**
|
|
* Extract a hex color value from a color definition node.
|
|
* Handles both `a:srgbClr@val` and `a:sysClr@lastClr`.
|
|
*/
|
|
function extractColor(node: SafeXmlNode): string | undefined {
|
|
const srgb = node.child('srgbClr')
|
|
if (srgb.exists()) {
|
|
return srgb.attr('val')
|
|
}
|
|
const sys = node.child('sysClr')
|
|
if (sys.exists()) {
|
|
return sys.attr('lastClr') ?? sys.attr('val')
|
|
}
|
|
return undefined
|
|
}
|
|
|
|
/**
|
|
* Parse font info from a majorFont or minorFont node.
|
|
* Extracts typeface attributes from latin, ea, and cs child elements.
|
|
*/
|
|
function parseFontInfo(fontNode: SafeXmlNode): { latin: string; ea: string; cs: string } {
|
|
return {
|
|
latin: fontNode.child('latin').attr('typeface') ?? '',
|
|
ea: fontNode.child('ea').attr('typeface') ?? '',
|
|
cs: fontNode.child('cs').attr('typeface') ?? '',
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse a theme XML root (`a:theme`) into ThemeData.
|
|
*/
|
|
export function parseTheme(root: SafeXmlNode): ThemeData {
|
|
const themeElements = root.child('themeElements')
|
|
|
|
// --- Color scheme ---
|
|
const clrScheme = themeElements.child('clrScheme')
|
|
const colorScheme = new Map<string, string>()
|
|
|
|
for (const slot of COLOR_SLOTS) {
|
|
const slotNode = clrScheme.child(slot)
|
|
if (slotNode.exists()) {
|
|
const hex = extractColor(slotNode)
|
|
if (hex !== undefined) {
|
|
colorScheme.set(slot, hex)
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Font scheme ---
|
|
const fontScheme = themeElements.child('fontScheme')
|
|
const majorFont = parseFontInfo(fontScheme.child('majorFont'))
|
|
const minorFont = parseFontInfo(fontScheme.child('minorFont'))
|
|
|
|
// --- Format scheme ---
|
|
const fmtScheme = themeElements.child('fmtScheme')
|
|
const fillStyleLst = fmtScheme.child('fillStyleLst')
|
|
const fillStyles: SafeXmlNode[] = fillStyleLst.allChildren()
|
|
const lnStyleLst = fmtScheme.child('lnStyleLst')
|
|
const lineStyles: SafeXmlNode[] = lnStyleLst.allChildren()
|
|
const effectStyleLst = fmtScheme.child('effectStyleLst')
|
|
const effectStyles: SafeXmlNode[] = effectStyleLst.allChildren()
|
|
|
|
return { colorScheme, majorFont, minorFont, fillStyles, lineStyles, effectStyles }
|
|
}
|